diff options
Diffstat (limited to 'net/switchdev/switchdev.c')
-rw-r--r-- | net/switchdev/switchdev.c | 827 |
1 files changed, 673 insertions, 154 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 055453d48668..ac853acbe211 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c | |||
@@ -15,97 +15,359 @@ | |||
15 | #include <linux/mutex.h> | 15 | #include <linux/mutex.h> |
16 | #include <linux/notifier.h> | 16 | #include <linux/notifier.h> |
17 | #include <linux/netdevice.h> | 17 | #include <linux/netdevice.h> |
18 | #include <linux/if_bridge.h> | ||
18 | #include <net/ip_fib.h> | 19 | #include <net/ip_fib.h> |
19 | #include <net/switchdev.h> | 20 | #include <net/switchdev.h> |
20 | 21 | ||
21 | /** | 22 | /** |
22 | * netdev_switch_parent_id_get - Get ID of a switch | 23 | * switchdev_port_attr_get - Get port attribute |
24 | * | ||
25 | * @dev: port device | ||
26 | * @attr: attribute to get | ||
27 | */ | ||
28 | int switchdev_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) | ||
29 | { | ||
30 | const struct switchdev_ops *ops = dev->switchdev_ops; | ||
31 | struct net_device *lower_dev; | ||
32 | struct list_head *iter; | ||
33 | struct switchdev_attr first = { | ||
34 | .id = SWITCHDEV_ATTR_UNDEFINED | ||
35 | }; | ||
36 | int err = -EOPNOTSUPP; | ||
37 | |||
38 | if (ops && ops->switchdev_port_attr_get) | ||
39 | return ops->switchdev_port_attr_get(dev, attr); | ||
40 | |||
41 | if (attr->flags & SWITCHDEV_F_NO_RECURSE) | ||
42 | return err; | ||
43 | |||
44 | /* Switch device port(s) may be stacked under | ||
45 | * bond/team/vlan dev, so recurse down to get attr on | ||
46 | * each port. Return -ENODATA if attr values don't | ||
47 | * compare across ports. | ||
48 | */ | ||
49 | |||
50 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
51 | err = switchdev_port_attr_get(lower_dev, attr); | ||
52 | if (err) | ||
53 | break; | ||
54 | if (first.id == SWITCHDEV_ATTR_UNDEFINED) | ||
55 | first = *attr; | ||
56 | else if (memcmp(&first, attr, sizeof(*attr))) | ||
57 | return -ENODATA; | ||
58 | } | ||
59 | |||
60 | return err; | ||
61 | } | ||
62 | EXPORT_SYMBOL_GPL(switchdev_port_attr_get); | ||
63 | |||
64 | static int __switchdev_port_attr_set(struct net_device *dev, | ||
65 | struct switchdev_attr *attr) | ||
66 | { | ||
67 | const struct switchdev_ops *ops = dev->switchdev_ops; | ||
68 | struct net_device *lower_dev; | ||
69 | struct list_head *iter; | ||
70 | int err = -EOPNOTSUPP; | ||
71 | |||
72 | if (ops && ops->switchdev_port_attr_set) | ||
73 | return ops->switchdev_port_attr_set(dev, attr); | ||
74 | |||
75 | if (attr->flags & SWITCHDEV_F_NO_RECURSE) | ||
76 | return err; | ||
77 | |||
78 | /* Switch device port(s) may be stacked under | ||
79 | * bond/team/vlan dev, so recurse down to set attr on | ||
80 | * each port. | ||
81 | */ | ||
82 | |||
83 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
84 | err = __switchdev_port_attr_set(lower_dev, attr); | ||
85 | if (err) | ||
86 | break; | ||
87 | } | ||
88 | |||
89 | return err; | ||
90 | } | ||
91 | |||
92 | struct switchdev_attr_set_work { | ||
93 | struct work_struct work; | ||
94 | struct net_device *dev; | ||
95 | struct switchdev_attr attr; | ||
96 | }; | ||
97 | |||
98 | static void switchdev_port_attr_set_work(struct work_struct *work) | ||
99 | { | ||
100 | struct switchdev_attr_set_work *asw = | ||
101 | container_of(work, struct switchdev_attr_set_work, work); | ||
102 | int err; | ||
103 | |||
104 | rtnl_lock(); | ||
105 | err = switchdev_port_attr_set(asw->dev, &asw->attr); | ||
106 | BUG_ON(err); | ||
107 | rtnl_unlock(); | ||
108 | |||
109 | dev_put(asw->dev); | ||
110 | kfree(work); | ||
111 | } | ||
112 | |||
113 | static int switchdev_port_attr_set_defer(struct net_device *dev, | ||
114 | struct switchdev_attr *attr) | ||
115 | { | ||
116 | struct switchdev_attr_set_work *asw; | ||
117 | |||
118 | asw = kmalloc(sizeof(*asw), GFP_ATOMIC); | ||
119 | if (!asw) | ||
120 | return -ENOMEM; | ||
121 | |||
122 | INIT_WORK(&asw->work, switchdev_port_attr_set_work); | ||
123 | |||
124 | dev_hold(dev); | ||
125 | asw->dev = dev; | ||
126 | memcpy(&asw->attr, attr, sizeof(asw->attr)); | ||
127 | |||
128 | schedule_work(&asw->work); | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * switchdev_port_attr_set - Set port attribute | ||
135 | * | ||
23 | * @dev: port device | 136 | * @dev: port device |
24 | * @psid: switch ID | 137 | * @attr: attribute to set |
25 | * | 138 | * |
26 | * Get ID of a switch this port is part of. | 139 | * Use a 2-phase prepare-commit transaction model to ensure |
140 | * system is not left in a partially updated state due to | ||
141 | * failure from driver/device. | ||
27 | */ | 142 | */ |
28 | int netdev_switch_parent_id_get(struct net_device *dev, | 143 | int switchdev_port_attr_set(struct net_device *dev, struct switchdev_attr *attr) |
29 | struct netdev_phys_item_id *psid) | ||
30 | { | 144 | { |
31 | const struct swdev_ops *ops = dev->swdev_ops; | 145 | int err; |
146 | |||
147 | if (!rtnl_is_locked()) { | ||
148 | /* Running prepare-commit transaction across stacked | ||
149 | * devices requires nothing moves, so if rtnl_lock is | ||
150 | * not held, schedule a worker thread to hold rtnl_lock | ||
151 | * while setting attr. | ||
152 | */ | ||
153 | |||
154 | return switchdev_port_attr_set_defer(dev, attr); | ||
155 | } | ||
156 | |||
157 | /* Phase I: prepare for attr set. Driver/device should fail | ||
158 | * here if there are going to be issues in the commit phase, | ||
159 | * such as lack of resources or support. The driver/device | ||
160 | * should reserve resources needed for the commit phase here, | ||
161 | * but should not commit the attr. | ||
162 | */ | ||
163 | |||
164 | attr->trans = SWITCHDEV_TRANS_PREPARE; | ||
165 | err = __switchdev_port_attr_set(dev, attr); | ||
166 | if (err) { | ||
167 | /* Prepare phase failed: abort the transaction. Any | ||
168 | * resources reserved in the prepare phase are | ||
169 | * released. | ||
170 | */ | ||
171 | |||
172 | attr->trans = SWITCHDEV_TRANS_ABORT; | ||
173 | __switchdev_port_attr_set(dev, attr); | ||
174 | |||
175 | return err; | ||
176 | } | ||
177 | |||
178 | /* Phase II: commit attr set. This cannot fail as a fault | ||
179 | * of driver/device. If it does, it's a bug in the driver/device | ||
180 | * because the driver said everythings was OK in phase I. | ||
181 | */ | ||
182 | |||
183 | attr->trans = SWITCHDEV_TRANS_COMMIT; | ||
184 | err = __switchdev_port_attr_set(dev, attr); | ||
185 | BUG_ON(err); | ||
186 | |||
187 | return err; | ||
188 | } | ||
189 | EXPORT_SYMBOL_GPL(switchdev_port_attr_set); | ||
190 | |||
191 | static int __switchdev_port_obj_add(struct net_device *dev, | ||
192 | struct switchdev_obj *obj) | ||
193 | { | ||
194 | const struct switchdev_ops *ops = dev->switchdev_ops; | ||
195 | struct net_device *lower_dev; | ||
196 | struct list_head *iter; | ||
197 | int err = -EOPNOTSUPP; | ||
198 | |||
199 | if (ops && ops->switchdev_port_obj_add) | ||
200 | return ops->switchdev_port_obj_add(dev, obj); | ||
201 | |||
202 | /* Switch device port(s) may be stacked under | ||
203 | * bond/team/vlan dev, so recurse down to add object on | ||
204 | * each port. | ||
205 | */ | ||
206 | |||
207 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
208 | err = __switchdev_port_obj_add(lower_dev, obj); | ||
209 | if (err) | ||
210 | break; | ||
211 | } | ||
212 | |||
213 | return err; | ||
214 | } | ||
215 | |||
216 | /** | ||
217 | * switchdev_port_obj_add - Add port object | ||
218 | * | ||
219 | * @dev: port device | ||
220 | * @obj: object to add | ||
221 | * | ||
222 | * Use a 2-phase prepare-commit transaction model to ensure | ||
223 | * system is not left in a partially updated state due to | ||
224 | * failure from driver/device. | ||
225 | * | ||
226 | * rtnl_lock must be held. | ||
227 | */ | ||
228 | int switchdev_port_obj_add(struct net_device *dev, struct switchdev_obj *obj) | ||
229 | { | ||
230 | int err; | ||
32 | 231 | ||
33 | if (!ops || !ops->swdev_parent_id_get) | 232 | ASSERT_RTNL(); |
34 | return -EOPNOTSUPP; | 233 | |
35 | return ops->swdev_parent_id_get(dev, psid); | 234 | /* Phase I: prepare for obj add. Driver/device should fail |
235 | * here if there are going to be issues in the commit phase, | ||
236 | * such as lack of resources or support. The driver/device | ||
237 | * should reserve resources needed for the commit phase here, | ||
238 | * but should not commit the obj. | ||
239 | */ | ||
240 | |||
241 | obj->trans = SWITCHDEV_TRANS_PREPARE; | ||
242 | err = __switchdev_port_obj_add(dev, obj); | ||
243 | if (err) { | ||
244 | /* Prepare phase failed: abort the transaction. Any | ||
245 | * resources reserved in the prepare phase are | ||
246 | * released. | ||
247 | */ | ||
248 | |||
249 | obj->trans = SWITCHDEV_TRANS_ABORT; | ||
250 | __switchdev_port_obj_add(dev, obj); | ||
251 | |||
252 | return err; | ||
253 | } | ||
254 | |||
255 | /* Phase II: commit obj add. This cannot fail as a fault | ||
256 | * of driver/device. If it does, it's a bug in the driver/device | ||
257 | * because the driver said everythings was OK in phase I. | ||
258 | */ | ||
259 | |||
260 | obj->trans = SWITCHDEV_TRANS_COMMIT; | ||
261 | err = __switchdev_port_obj_add(dev, obj); | ||
262 | WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id); | ||
263 | |||
264 | return err; | ||
36 | } | 265 | } |
37 | EXPORT_SYMBOL_GPL(netdev_switch_parent_id_get); | 266 | EXPORT_SYMBOL_GPL(switchdev_port_obj_add); |
38 | 267 | ||
39 | /** | 268 | /** |
40 | * netdev_switch_port_stp_update - Notify switch device port of STP | 269 | * switchdev_port_obj_del - Delete port object |
41 | * state change | 270 | * |
42 | * @dev: port device | 271 | * @dev: port device |
43 | * @state: port STP state | 272 | * @obj: object to delete |
273 | */ | ||
274 | int switchdev_port_obj_del(struct net_device *dev, struct switchdev_obj *obj) | ||
275 | { | ||
276 | const struct switchdev_ops *ops = dev->switchdev_ops; | ||
277 | struct net_device *lower_dev; | ||
278 | struct list_head *iter; | ||
279 | int err = -EOPNOTSUPP; | ||
280 | |||
281 | if (ops && ops->switchdev_port_obj_del) | ||
282 | return ops->switchdev_port_obj_del(dev, obj); | ||
283 | |||
284 | /* Switch device port(s) may be stacked under | ||
285 | * bond/team/vlan dev, so recurse down to delete object on | ||
286 | * each port. | ||
287 | */ | ||
288 | |||
289 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
290 | err = switchdev_port_obj_del(lower_dev, obj); | ||
291 | if (err) | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | return err; | ||
296 | } | ||
297 | EXPORT_SYMBOL_GPL(switchdev_port_obj_del); | ||
298 | |||
299 | /** | ||
300 | * switchdev_port_obj_dump - Dump port objects | ||
44 | * | 301 | * |
45 | * Notify switch device port of bridge port STP state change. | 302 | * @dev: port device |
303 | * @obj: object to dump | ||
46 | */ | 304 | */ |
47 | int netdev_switch_port_stp_update(struct net_device *dev, u8 state) | 305 | int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj) |
48 | { | 306 | { |
49 | const struct swdev_ops *ops = dev->swdev_ops; | 307 | const struct switchdev_ops *ops = dev->switchdev_ops; |
50 | struct net_device *lower_dev; | 308 | struct net_device *lower_dev; |
51 | struct list_head *iter; | 309 | struct list_head *iter; |
52 | int err = -EOPNOTSUPP; | 310 | int err = -EOPNOTSUPP; |
53 | 311 | ||
54 | if (ops && ops->swdev_port_stp_update) | 312 | if (ops && ops->switchdev_port_obj_dump) |
55 | return ops->swdev_port_stp_update(dev, state); | 313 | return ops->switchdev_port_obj_dump(dev, obj); |
314 | |||
315 | /* Switch device port(s) may be stacked under | ||
316 | * bond/team/vlan dev, so recurse down to dump objects on | ||
317 | * first port at bottom of stack. | ||
318 | */ | ||
56 | 319 | ||
57 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | 320 | netdev_for_each_lower_dev(dev, lower_dev, iter) { |
58 | err = netdev_switch_port_stp_update(lower_dev, state); | 321 | err = switchdev_port_obj_dump(lower_dev, obj); |
59 | if (err && err != -EOPNOTSUPP) | 322 | break; |
60 | return err; | ||
61 | } | 323 | } |
62 | 324 | ||
63 | return err; | 325 | return err; |
64 | } | 326 | } |
65 | EXPORT_SYMBOL_GPL(netdev_switch_port_stp_update); | 327 | EXPORT_SYMBOL_GPL(switchdev_port_obj_dump); |
66 | 328 | ||
67 | static DEFINE_MUTEX(netdev_switch_mutex); | 329 | static DEFINE_MUTEX(switchdev_mutex); |
68 | static RAW_NOTIFIER_HEAD(netdev_switch_notif_chain); | 330 | static RAW_NOTIFIER_HEAD(switchdev_notif_chain); |
69 | 331 | ||
70 | /** | 332 | /** |
71 | * register_netdev_switch_notifier - Register notifier | 333 | * register_switchdev_notifier - Register notifier |
72 | * @nb: notifier_block | 334 | * @nb: notifier_block |
73 | * | 335 | * |
74 | * Register switch device notifier. This should be used by code | 336 | * Register switch device notifier. This should be used by code |
75 | * which needs to monitor events happening in particular device. | 337 | * which needs to monitor events happening in particular device. |
76 | * Return values are same as for atomic_notifier_chain_register(). | 338 | * Return values are same as for atomic_notifier_chain_register(). |
77 | */ | 339 | */ |
78 | int register_netdev_switch_notifier(struct notifier_block *nb) | 340 | int register_switchdev_notifier(struct notifier_block *nb) |
79 | { | 341 | { |
80 | int err; | 342 | int err; |
81 | 343 | ||
82 | mutex_lock(&netdev_switch_mutex); | 344 | mutex_lock(&switchdev_mutex); |
83 | err = raw_notifier_chain_register(&netdev_switch_notif_chain, nb); | 345 | err = raw_notifier_chain_register(&switchdev_notif_chain, nb); |
84 | mutex_unlock(&netdev_switch_mutex); | 346 | mutex_unlock(&switchdev_mutex); |
85 | return err; | 347 | return err; |
86 | } | 348 | } |
87 | EXPORT_SYMBOL_GPL(register_netdev_switch_notifier); | 349 | EXPORT_SYMBOL_GPL(register_switchdev_notifier); |
88 | 350 | ||
89 | /** | 351 | /** |
90 | * unregister_netdev_switch_notifier - Unregister notifier | 352 | * unregister_switchdev_notifier - Unregister notifier |
91 | * @nb: notifier_block | 353 | * @nb: notifier_block |
92 | * | 354 | * |
93 | * Unregister switch device notifier. | 355 | * Unregister switch device notifier. |
94 | * Return values are same as for atomic_notifier_chain_unregister(). | 356 | * Return values are same as for atomic_notifier_chain_unregister(). |
95 | */ | 357 | */ |
96 | int unregister_netdev_switch_notifier(struct notifier_block *nb) | 358 | int unregister_switchdev_notifier(struct notifier_block *nb) |
97 | { | 359 | { |
98 | int err; | 360 | int err; |
99 | 361 | ||
100 | mutex_lock(&netdev_switch_mutex); | 362 | mutex_lock(&switchdev_mutex); |
101 | err = raw_notifier_chain_unregister(&netdev_switch_notif_chain, nb); | 363 | err = raw_notifier_chain_unregister(&switchdev_notif_chain, nb); |
102 | mutex_unlock(&netdev_switch_mutex); | 364 | mutex_unlock(&switchdev_mutex); |
103 | return err; | 365 | return err; |
104 | } | 366 | } |
105 | EXPORT_SYMBOL_GPL(unregister_netdev_switch_notifier); | 367 | EXPORT_SYMBOL_GPL(unregister_switchdev_notifier); |
106 | 368 | ||
107 | /** | 369 | /** |
108 | * call_netdev_switch_notifiers - Call notifiers | 370 | * call_switchdev_notifiers - Call notifiers |
109 | * @val: value passed unmodified to notifier function | 371 | * @val: value passed unmodified to notifier function |
110 | * @dev: port device | 372 | * @dev: port device |
111 | * @info: notifier information data | 373 | * @info: notifier information data |
@@ -114,146 +376,387 @@ EXPORT_SYMBOL_GPL(unregister_netdev_switch_notifier); | |||
114 | * when it needs to propagate hardware event. | 376 | * when it needs to propagate hardware event. |
115 | * Return values are same as for atomic_notifier_call_chain(). | 377 | * Return values are same as for atomic_notifier_call_chain(). |
116 | */ | 378 | */ |
117 | int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev, | 379 | int call_switchdev_notifiers(unsigned long val, struct net_device *dev, |
118 | struct netdev_switch_notifier_info *info) | 380 | struct switchdev_notifier_info *info) |
119 | { | 381 | { |
120 | int err; | 382 | int err; |
121 | 383 | ||
122 | info->dev = dev; | 384 | info->dev = dev; |
123 | mutex_lock(&netdev_switch_mutex); | 385 | mutex_lock(&switchdev_mutex); |
124 | err = raw_notifier_call_chain(&netdev_switch_notif_chain, val, info); | 386 | err = raw_notifier_call_chain(&switchdev_notif_chain, val, info); |
125 | mutex_unlock(&netdev_switch_mutex); | 387 | mutex_unlock(&switchdev_mutex); |
126 | return err; | 388 | return err; |
127 | } | 389 | } |
128 | EXPORT_SYMBOL_GPL(call_netdev_switch_notifiers); | 390 | EXPORT_SYMBOL_GPL(call_switchdev_notifiers); |
129 | 391 | ||
130 | /** | 392 | /** |
131 | * netdev_switch_port_bridge_setlink - Notify switch device port of bridge | 393 | * switchdev_port_bridge_getlink - Get bridge port attributes |
132 | * port attributes | ||
133 | * | 394 | * |
134 | * @dev: port device | 395 | * @dev: port device |
135 | * @nlh: netlink msg with bridge port attributes | ||
136 | * @flags: bridge setlink flags | ||
137 | * | 396 | * |
138 | * Notify switch device port of bridge port attributes | 397 | * Called for SELF on rtnl_bridge_getlink to get bridge port |
398 | * attributes. | ||
139 | */ | 399 | */ |
140 | int netdev_switch_port_bridge_setlink(struct net_device *dev, | 400 | int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, |
141 | struct nlmsghdr *nlh, u16 flags) | 401 | struct net_device *dev, u32 filter_mask, |
402 | int nlflags) | ||
142 | { | 403 | { |
143 | const struct net_device_ops *ops = dev->netdev_ops; | 404 | struct switchdev_attr attr = { |
405 | .id = SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS, | ||
406 | }; | ||
407 | u16 mode = BRIDGE_MODE_UNDEF; | ||
408 | u32 mask = BR_LEARNING | BR_LEARNING_SYNC; | ||
409 | int err; | ||
144 | 410 | ||
145 | if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) | 411 | err = switchdev_port_attr_get(dev, &attr); |
146 | return 0; | 412 | if (err) |
413 | return err; | ||
414 | |||
415 | return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, | ||
416 | attr.u.brport_flags, mask, nlflags); | ||
417 | } | ||
418 | EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink); | ||
419 | |||
420 | static int switchdev_port_br_setflag(struct net_device *dev, | ||
421 | struct nlattr *nlattr, | ||
422 | unsigned long brport_flag) | ||
423 | { | ||
424 | struct switchdev_attr attr = { | ||
425 | .id = SWITCHDEV_ATTR_PORT_BRIDGE_FLAGS, | ||
426 | }; | ||
427 | u8 flag = nla_get_u8(nlattr); | ||
428 | int err; | ||
429 | |||
430 | err = switchdev_port_attr_get(dev, &attr); | ||
431 | if (err) | ||
432 | return err; | ||
433 | |||
434 | if (flag) | ||
435 | attr.u.brport_flags |= brport_flag; | ||
436 | else | ||
437 | attr.u.brport_flags &= ~brport_flag; | ||
438 | |||
439 | return switchdev_port_attr_set(dev, &attr); | ||
440 | } | ||
441 | |||
442 | static const struct nla_policy | ||
443 | switchdev_port_bridge_policy[IFLA_BRPORT_MAX + 1] = { | ||
444 | [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, | ||
445 | [IFLA_BRPORT_COST] = { .type = NLA_U32 }, | ||
446 | [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, | ||
447 | [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, | ||
448 | [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, | ||
449 | [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, | ||
450 | [IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 }, | ||
451 | [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, | ||
452 | [IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 }, | ||
453 | [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, | ||
454 | }; | ||
455 | |||
456 | static int switchdev_port_br_setlink_protinfo(struct net_device *dev, | ||
457 | struct nlattr *protinfo) | ||
458 | { | ||
459 | struct nlattr *attr; | ||
460 | int rem; | ||
461 | int err; | ||
462 | |||
463 | err = nla_validate_nested(protinfo, IFLA_BRPORT_MAX, | ||
464 | switchdev_port_bridge_policy); | ||
465 | if (err) | ||
466 | return err; | ||
467 | |||
468 | nla_for_each_nested(attr, protinfo, rem) { | ||
469 | switch (nla_type(attr)) { | ||
470 | case IFLA_BRPORT_LEARNING: | ||
471 | err = switchdev_port_br_setflag(dev, attr, | ||
472 | BR_LEARNING); | ||
473 | break; | ||
474 | case IFLA_BRPORT_LEARNING_SYNC: | ||
475 | err = switchdev_port_br_setflag(dev, attr, | ||
476 | BR_LEARNING_SYNC); | ||
477 | break; | ||
478 | default: | ||
479 | err = -EOPNOTSUPP; | ||
480 | break; | ||
481 | } | ||
482 | if (err) | ||
483 | return err; | ||
484 | } | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | static int switchdev_port_br_afspec(struct net_device *dev, | ||
490 | struct nlattr *afspec, | ||
491 | int (*f)(struct net_device *dev, | ||
492 | struct switchdev_obj *obj)) | ||
493 | { | ||
494 | struct nlattr *attr; | ||
495 | struct bridge_vlan_info *vinfo; | ||
496 | struct switchdev_obj obj = { | ||
497 | .id = SWITCHDEV_OBJ_PORT_VLAN, | ||
498 | }; | ||
499 | struct switchdev_obj_vlan *vlan = &obj.u.vlan; | ||
500 | int rem; | ||
501 | int err; | ||
147 | 502 | ||
148 | if (!ops->ndo_bridge_setlink) | 503 | nla_for_each_nested(attr, afspec, rem) { |
149 | return -EOPNOTSUPP; | 504 | if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) |
505 | continue; | ||
506 | if (nla_len(attr) != sizeof(struct bridge_vlan_info)) | ||
507 | return -EINVAL; | ||
508 | vinfo = nla_data(attr); | ||
509 | vlan->flags = vinfo->flags; | ||
510 | if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { | ||
511 | if (vlan->vid_start) | ||
512 | return -EINVAL; | ||
513 | vlan->vid_start = vinfo->vid; | ||
514 | } else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) { | ||
515 | if (!vlan->vid_start) | ||
516 | return -EINVAL; | ||
517 | vlan->vid_end = vinfo->vid; | ||
518 | if (vlan->vid_end <= vlan->vid_start) | ||
519 | return -EINVAL; | ||
520 | err = f(dev, &obj); | ||
521 | if (err) | ||
522 | return err; | ||
523 | memset(vlan, 0, sizeof(*vlan)); | ||
524 | } else { | ||
525 | if (vlan->vid_start) | ||
526 | return -EINVAL; | ||
527 | vlan->vid_start = vinfo->vid; | ||
528 | vlan->vid_end = vinfo->vid; | ||
529 | err = f(dev, &obj); | ||
530 | if (err) | ||
531 | return err; | ||
532 | memset(vlan, 0, sizeof(*vlan)); | ||
533 | } | ||
534 | } | ||
150 | 535 | ||
151 | return ops->ndo_bridge_setlink(dev, nlh, flags); | 536 | return 0; |
152 | } | 537 | } |
153 | EXPORT_SYMBOL_GPL(netdev_switch_port_bridge_setlink); | ||
154 | 538 | ||
155 | /** | 539 | /** |
156 | * netdev_switch_port_bridge_dellink - Notify switch device port of bridge | 540 | * switchdev_port_bridge_setlink - Set bridge port attributes |
157 | * port attribute delete | ||
158 | * | 541 | * |
159 | * @dev: port device | 542 | * @dev: port device |
160 | * @nlh: netlink msg with bridge port attributes | 543 | * @nlh: netlink header |
161 | * @flags: bridge setlink flags | 544 | * @flags: netlink flags |
162 | * | 545 | * |
163 | * Notify switch device port of bridge port attribute delete | 546 | * Called for SELF on rtnl_bridge_setlink to set bridge port |
547 | * attributes. | ||
164 | */ | 548 | */ |
165 | int netdev_switch_port_bridge_dellink(struct net_device *dev, | 549 | int switchdev_port_bridge_setlink(struct net_device *dev, |
166 | struct nlmsghdr *nlh, u16 flags) | 550 | struct nlmsghdr *nlh, u16 flags) |
167 | { | 551 | { |
168 | const struct net_device_ops *ops = dev->netdev_ops; | 552 | struct nlattr *protinfo; |
553 | struct nlattr *afspec; | ||
554 | int err = 0; | ||
169 | 555 | ||
170 | if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) | 556 | protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), |
171 | return 0; | 557 | IFLA_PROTINFO); |
558 | if (protinfo) { | ||
559 | err = switchdev_port_br_setlink_protinfo(dev, protinfo); | ||
560 | if (err) | ||
561 | return err; | ||
562 | } | ||
172 | 563 | ||
173 | if (!ops->ndo_bridge_dellink) | 564 | afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), |
174 | return -EOPNOTSUPP; | 565 | IFLA_AF_SPEC); |
566 | if (afspec) | ||
567 | err = switchdev_port_br_afspec(dev, afspec, | ||
568 | switchdev_port_obj_add); | ||
175 | 569 | ||
176 | return ops->ndo_bridge_dellink(dev, nlh, flags); | 570 | return err; |
177 | } | 571 | } |
178 | EXPORT_SYMBOL_GPL(netdev_switch_port_bridge_dellink); | 572 | EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink); |
179 | 573 | ||
180 | /** | 574 | /** |
181 | * ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink | 575 | * switchdev_port_bridge_dellink - Set bridge port attributes |
182 | * op for master devices | ||
183 | * | 576 | * |
184 | * @dev: port device | 577 | * @dev: port device |
185 | * @nlh: netlink msg with bridge port attributes | 578 | * @nlh: netlink header |
186 | * @flags: bridge setlink flags | 579 | * @flags: netlink flags |
187 | * | 580 | * |
188 | * Notify master device slaves of bridge port attributes | 581 | * Called for SELF on rtnl_bridge_dellink to set bridge port |
582 | * attributes. | ||
189 | */ | 583 | */ |
190 | int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev, | 584 | int switchdev_port_bridge_dellink(struct net_device *dev, |
191 | struct nlmsghdr *nlh, u16 flags) | 585 | struct nlmsghdr *nlh, u16 flags) |
192 | { | 586 | { |
193 | struct net_device *lower_dev; | 587 | struct nlattr *afspec; |
194 | struct list_head *iter; | ||
195 | int ret = 0, err = 0; | ||
196 | 588 | ||
197 | if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) | 589 | afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), |
198 | return ret; | 590 | IFLA_AF_SPEC); |
591 | if (afspec) | ||
592 | return switchdev_port_br_afspec(dev, afspec, | ||
593 | switchdev_port_obj_del); | ||
199 | 594 | ||
200 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | 595 | return 0; |
201 | err = netdev_switch_port_bridge_setlink(lower_dev, nlh, flags); | 596 | } |
202 | if (err && err != -EOPNOTSUPP) | 597 | EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink); |
203 | ret = err; | ||
204 | } | ||
205 | 598 | ||
206 | return ret; | 599 | /** |
600 | * switchdev_port_fdb_add - Add FDB (MAC/VLAN) entry to port | ||
601 | * | ||
602 | * @ndmsg: netlink hdr | ||
603 | * @nlattr: netlink attributes | ||
604 | * @dev: port device | ||
605 | * @addr: MAC address to add | ||
606 | * @vid: VLAN to add | ||
607 | * | ||
608 | * Add FDB entry to switch device. | ||
609 | */ | ||
610 | int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], | ||
611 | struct net_device *dev, const unsigned char *addr, | ||
612 | u16 vid, u16 nlm_flags) | ||
613 | { | ||
614 | struct switchdev_obj obj = { | ||
615 | .id = SWITCHDEV_OBJ_PORT_FDB, | ||
616 | .u.fdb = { | ||
617 | .addr = addr, | ||
618 | .vid = vid, | ||
619 | }, | ||
620 | }; | ||
621 | |||
622 | return switchdev_port_obj_add(dev, &obj); | ||
207 | } | 623 | } |
208 | EXPORT_SYMBOL_GPL(ndo_dflt_netdev_switch_port_bridge_setlink); | 624 | EXPORT_SYMBOL_GPL(switchdev_port_fdb_add); |
209 | 625 | ||
210 | /** | 626 | /** |
211 | * ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink | 627 | * switchdev_port_fdb_del - Delete FDB (MAC/VLAN) entry from port |
212 | * op for master devices | ||
213 | * | 628 | * |
629 | * @ndmsg: netlink hdr | ||
630 | * @nlattr: netlink attributes | ||
214 | * @dev: port device | 631 | * @dev: port device |
215 | * @nlh: netlink msg with bridge port attributes | 632 | * @addr: MAC address to delete |
216 | * @flags: bridge dellink flags | 633 | * @vid: VLAN to delete |
217 | * | 634 | * |
218 | * Notify master device slaves of bridge port attribute deletes | 635 | * Delete FDB entry from switch device. |
219 | */ | 636 | */ |
220 | int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev, | 637 | int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], |
221 | struct nlmsghdr *nlh, u16 flags) | 638 | struct net_device *dev, const unsigned char *addr, |
639 | u16 vid) | ||
222 | { | 640 | { |
223 | struct net_device *lower_dev; | 641 | struct switchdev_obj obj = { |
224 | struct list_head *iter; | 642 | .id = SWITCHDEV_OBJ_PORT_FDB, |
225 | int ret = 0, err = 0; | 643 | .u.fdb = { |
644 | .addr = addr, | ||
645 | .vid = vid, | ||
646 | }, | ||
647 | }; | ||
648 | |||
649 | return switchdev_port_obj_del(dev, &obj); | ||
650 | } | ||
651 | EXPORT_SYMBOL_GPL(switchdev_port_fdb_del); | ||
652 | |||
653 | struct switchdev_fdb_dump { | ||
654 | struct switchdev_obj obj; | ||
655 | struct sk_buff *skb; | ||
656 | struct netlink_callback *cb; | ||
657 | struct net_device *filter_dev; | ||
658 | int idx; | ||
659 | }; | ||
660 | |||
661 | static int switchdev_port_fdb_dump_cb(struct net_device *dev, | ||
662 | struct switchdev_obj *obj) | ||
663 | { | ||
664 | struct switchdev_fdb_dump *dump = | ||
665 | container_of(obj, struct switchdev_fdb_dump, obj); | ||
666 | u32 portid = NETLINK_CB(dump->cb->skb).portid; | ||
667 | u32 seq = dump->cb->nlh->nlmsg_seq; | ||
668 | struct nlmsghdr *nlh; | ||
669 | struct ndmsg *ndm; | ||
670 | struct net_device *master = netdev_master_upper_dev_get(dev); | ||
671 | |||
672 | if (dump->idx < dump->cb->args[0]) | ||
673 | goto skip; | ||
674 | |||
675 | if (master && dump->filter_dev != master) | ||
676 | goto skip; | ||
677 | |||
678 | nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, | ||
679 | sizeof(*ndm), NLM_F_MULTI); | ||
680 | if (!nlh) | ||
681 | return -EMSGSIZE; | ||
682 | |||
683 | ndm = nlmsg_data(nlh); | ||
684 | ndm->ndm_family = AF_BRIDGE; | ||
685 | ndm->ndm_pad1 = 0; | ||
686 | ndm->ndm_pad2 = 0; | ||
687 | ndm->ndm_flags = NTF_SELF; | ||
688 | ndm->ndm_type = 0; | ||
689 | ndm->ndm_ifindex = dev->ifindex; | ||
690 | ndm->ndm_state = NUD_REACHABLE; | ||
691 | |||
692 | if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, obj->u.fdb.addr)) | ||
693 | goto nla_put_failure; | ||
694 | |||
695 | if (obj->u.fdb.vid && nla_put_u16(dump->skb, NDA_VLAN, obj->u.fdb.vid)) | ||
696 | goto nla_put_failure; | ||
697 | |||
698 | nlmsg_end(dump->skb, nlh); | ||
699 | |||
700 | skip: | ||
701 | dump->idx++; | ||
702 | return 0; | ||
703 | |||
704 | nla_put_failure: | ||
705 | nlmsg_cancel(dump->skb, nlh); | ||
706 | return -EMSGSIZE; | ||
707 | } | ||
226 | 708 | ||
227 | if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD)) | 709 | /** |
228 | return ret; | 710 | * switchdev_port_fdb_dump - Dump port FDB (MAC/VLAN) entries |
711 | * | ||
712 | * @skb: netlink skb | ||
713 | * @cb: netlink callback | ||
714 | * @dev: port device | ||
715 | * @filter_dev: filter device | ||
716 | * @idx: | ||
717 | * | ||
718 | * Delete FDB entry from switch device. | ||
719 | */ | ||
720 | int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, | ||
721 | struct net_device *dev, | ||
722 | struct net_device *filter_dev, int idx) | ||
723 | { | ||
724 | struct switchdev_fdb_dump dump = { | ||
725 | .obj = { | ||
726 | .id = SWITCHDEV_OBJ_PORT_FDB, | ||
727 | .cb = switchdev_port_fdb_dump_cb, | ||
728 | }, | ||
729 | .skb = skb, | ||
730 | .cb = cb, | ||
731 | .filter_dev = filter_dev, | ||
732 | .idx = idx, | ||
733 | }; | ||
734 | int err; | ||
229 | 735 | ||
230 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | 736 | err = switchdev_port_obj_dump(dev, &dump.obj); |
231 | err = netdev_switch_port_bridge_dellink(lower_dev, nlh, flags); | 737 | if (err) |
232 | if (err && err != -EOPNOTSUPP) | 738 | return err; |
233 | ret = err; | ||
234 | } | ||
235 | 739 | ||
236 | return ret; | 740 | return dump.idx; |
237 | } | 741 | } |
238 | EXPORT_SYMBOL_GPL(ndo_dflt_netdev_switch_port_bridge_dellink); | 742 | EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump); |
239 | 743 | ||
240 | static struct net_device *netdev_switch_get_lowest_dev(struct net_device *dev) | 744 | static struct net_device *switchdev_get_lowest_dev(struct net_device *dev) |
241 | { | 745 | { |
242 | const struct swdev_ops *ops = dev->swdev_ops; | 746 | const struct switchdev_ops *ops = dev->switchdev_ops; |
243 | struct net_device *lower_dev; | 747 | struct net_device *lower_dev; |
244 | struct net_device *port_dev; | 748 | struct net_device *port_dev; |
245 | struct list_head *iter; | 749 | struct list_head *iter; |
246 | 750 | ||
247 | /* Recusively search down until we find a sw port dev. | 751 | /* Recusively search down until we find a sw port dev. |
248 | * (A sw port dev supports swdev_parent_id_get). | 752 | * (A sw port dev supports switchdev_port_attr_get). |
249 | */ | 753 | */ |
250 | 754 | ||
251 | if (dev->features & NETIF_F_HW_SWITCH_OFFLOAD && | 755 | if (ops && ops->switchdev_port_attr_get) |
252 | ops && ops->swdev_parent_id_get) | ||
253 | return dev; | 756 | return dev; |
254 | 757 | ||
255 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | 758 | netdev_for_each_lower_dev(dev, lower_dev, iter) { |
256 | port_dev = netdev_switch_get_lowest_dev(lower_dev); | 759 | port_dev = switchdev_get_lowest_dev(lower_dev); |
257 | if (port_dev) | 760 | if (port_dev) |
258 | return port_dev; | 761 | return port_dev; |
259 | } | 762 | } |
@@ -261,10 +764,12 @@ static struct net_device *netdev_switch_get_lowest_dev(struct net_device *dev) | |||
261 | return NULL; | 764 | return NULL; |
262 | } | 765 | } |
263 | 766 | ||
264 | static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi) | 767 | static struct net_device *switchdev_get_dev_by_nhs(struct fib_info *fi) |
265 | { | 768 | { |
266 | struct netdev_phys_item_id psid; | 769 | struct switchdev_attr attr = { |
267 | struct netdev_phys_item_id prev_psid; | 770 | .id = SWITCHDEV_ATTR_PORT_PARENT_ID, |
771 | }; | ||
772 | struct switchdev_attr prev_attr; | ||
268 | struct net_device *dev = NULL; | 773 | struct net_device *dev = NULL; |
269 | int nhsel; | 774 | int nhsel; |
270 | 775 | ||
@@ -276,28 +781,29 @@ static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi) | |||
276 | if (!nh->nh_dev) | 781 | if (!nh->nh_dev) |
277 | return NULL; | 782 | return NULL; |
278 | 783 | ||
279 | dev = netdev_switch_get_lowest_dev(nh->nh_dev); | 784 | dev = switchdev_get_lowest_dev(nh->nh_dev); |
280 | if (!dev) | 785 | if (!dev) |
281 | return NULL; | 786 | return NULL; |
282 | 787 | ||
283 | if (netdev_switch_parent_id_get(dev, &psid)) | 788 | if (switchdev_port_attr_get(dev, &attr)) |
284 | return NULL; | 789 | return NULL; |
285 | 790 | ||
286 | if (nhsel > 0) { | 791 | if (nhsel > 0) { |
287 | if (prev_psid.id_len != psid.id_len) | 792 | if (prev_attr.u.ppid.id_len != attr.u.ppid.id_len) |
288 | return NULL; | 793 | return NULL; |
289 | if (memcmp(prev_psid.id, psid.id, psid.id_len)) | 794 | if (memcmp(prev_attr.u.ppid.id, attr.u.ppid.id, |
795 | attr.u.ppid.id_len)) | ||
290 | return NULL; | 796 | return NULL; |
291 | } | 797 | } |
292 | 798 | ||
293 | prev_psid = psid; | 799 | prev_attr = attr; |
294 | } | 800 | } |
295 | 801 | ||
296 | return dev; | 802 | return dev; |
297 | } | 803 | } |
298 | 804 | ||
299 | /** | 805 | /** |
300 | * netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch | 806 | * switchdev_fib_ipv4_add - Add IPv4 route entry to switch |
301 | * | 807 | * |
302 | * @dst: route's IPv4 destination address | 808 | * @dst: route's IPv4 destination address |
303 | * @dst_len: destination address length (prefix length) | 809 | * @dst_len: destination address length (prefix length) |
@@ -309,11 +815,22 @@ static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi) | |||
309 | * | 815 | * |
310 | * Add IPv4 route entry to switch device. | 816 | * Add IPv4 route entry to switch device. |
311 | */ | 817 | */ |
312 | int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, | 818 | int switchdev_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, |
313 | u8 tos, u8 type, u32 nlflags, u32 tb_id) | 819 | u8 tos, u8 type, u32 nlflags, u32 tb_id) |
314 | { | 820 | { |
821 | struct switchdev_obj fib_obj = { | ||
822 | .id = SWITCHDEV_OBJ_IPV4_FIB, | ||
823 | .u.ipv4_fib = { | ||
824 | .dst = dst, | ||
825 | .dst_len = dst_len, | ||
826 | .fi = fi, | ||
827 | .tos = tos, | ||
828 | .type = type, | ||
829 | .nlflags = nlflags, | ||
830 | .tb_id = tb_id, | ||
831 | }, | ||
832 | }; | ||
315 | struct net_device *dev; | 833 | struct net_device *dev; |
316 | const struct swdev_ops *ops; | ||
317 | int err = 0; | 834 | int err = 0; |
318 | 835 | ||
319 | /* Don't offload route if using custom ip rules or if | 836 | /* Don't offload route if using custom ip rules or if |
@@ -328,25 +845,20 @@ int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, | |||
328 | if (fi->fib_net->ipv4.fib_offload_disabled) | 845 | if (fi->fib_net->ipv4.fib_offload_disabled) |
329 | return 0; | 846 | return 0; |
330 | 847 | ||
331 | dev = netdev_switch_get_dev_by_nhs(fi); | 848 | dev = switchdev_get_dev_by_nhs(fi); |
332 | if (!dev) | 849 | if (!dev) |
333 | return 0; | 850 | return 0; |
334 | ops = dev->swdev_ops; | 851 | |
335 | 852 | err = switchdev_port_obj_add(dev, &fib_obj); | |
336 | if (ops->swdev_fib_ipv4_add) { | 853 | if (!err) |
337 | err = ops->swdev_fib_ipv4_add(dev, htonl(dst), dst_len, | 854 | fi->fib_flags |= RTNH_F_OFFLOAD; |
338 | fi, tos, type, nlflags, | ||
339 | tb_id); | ||
340 | if (!err) | ||
341 | fi->fib_flags |= RTNH_F_OFFLOAD; | ||
342 | } | ||
343 | 855 | ||
344 | return err; | 856 | return err; |
345 | } | 857 | } |
346 | EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_add); | 858 | EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_add); |
347 | 859 | ||
348 | /** | 860 | /** |
349 | * netdev_switch_fib_ipv4_del - Delete IPv4 route entry from switch | 861 | * switchdev_fib_ipv4_del - Delete IPv4 route entry from switch |
350 | * | 862 | * |
351 | * @dst: route's IPv4 destination address | 863 | * @dst: route's IPv4 destination address |
352 | * @dst_len: destination address length (prefix length) | 864 | * @dst_len: destination address length (prefix length) |
@@ -357,38 +869,45 @@ EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_add); | |||
357 | * | 869 | * |
358 | * Delete IPv4 route entry from switch device. | 870 | * Delete IPv4 route entry from switch device. |
359 | */ | 871 | */ |
360 | int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, | 872 | int switchdev_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, |
361 | u8 tos, u8 type, u32 tb_id) | 873 | u8 tos, u8 type, u32 tb_id) |
362 | { | 874 | { |
875 | struct switchdev_obj fib_obj = { | ||
876 | .id = SWITCHDEV_OBJ_IPV4_FIB, | ||
877 | .u.ipv4_fib = { | ||
878 | .dst = dst, | ||
879 | .dst_len = dst_len, | ||
880 | .fi = fi, | ||
881 | .tos = tos, | ||
882 | .type = type, | ||
883 | .nlflags = 0, | ||
884 | .tb_id = tb_id, | ||
885 | }, | ||
886 | }; | ||
363 | struct net_device *dev; | 887 | struct net_device *dev; |
364 | const struct swdev_ops *ops; | ||
365 | int err = 0; | 888 | int err = 0; |
366 | 889 | ||
367 | if (!(fi->fib_flags & RTNH_F_OFFLOAD)) | 890 | if (!(fi->fib_flags & RTNH_F_OFFLOAD)) |
368 | return 0; | 891 | return 0; |
369 | 892 | ||
370 | dev = netdev_switch_get_dev_by_nhs(fi); | 893 | dev = switchdev_get_dev_by_nhs(fi); |
371 | if (!dev) | 894 | if (!dev) |
372 | return 0; | 895 | return 0; |
373 | ops = dev->swdev_ops; | ||
374 | 896 | ||
375 | if (ops->swdev_fib_ipv4_del) { | 897 | err = switchdev_port_obj_del(dev, &fib_obj); |
376 | err = ops->swdev_fib_ipv4_del(dev, htonl(dst), dst_len, | 898 | if (!err) |
377 | fi, tos, type, tb_id); | 899 | fi->fib_flags &= ~RTNH_F_OFFLOAD; |
378 | if (!err) | ||
379 | fi->fib_flags &= ~RTNH_F_OFFLOAD; | ||
380 | } | ||
381 | 900 | ||
382 | return err; | 901 | return err; |
383 | } | 902 | } |
384 | EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_del); | 903 | EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_del); |
385 | 904 | ||
386 | /** | 905 | /** |
387 | * netdev_switch_fib_ipv4_abort - Abort an IPv4 FIB operation | 906 | * switchdev_fib_ipv4_abort - Abort an IPv4 FIB operation |
388 | * | 907 | * |
389 | * @fi: route FIB info structure | 908 | * @fi: route FIB info structure |
390 | */ | 909 | */ |
391 | void netdev_switch_fib_ipv4_abort(struct fib_info *fi) | 910 | void switchdev_fib_ipv4_abort(struct fib_info *fi) |
392 | { | 911 | { |
393 | /* There was a problem installing this route to the offload | 912 | /* There was a problem installing this route to the offload |
394 | * device. For now, until we come up with more refined | 913 | * device. For now, until we come up with more refined |
@@ -401,4 +920,4 @@ void netdev_switch_fib_ipv4_abort(struct fib_info *fi) | |||
401 | fib_flush_external(fi->fib_net); | 920 | fib_flush_external(fi->fib_net); |
402 | fi->fib_net->ipv4.fib_offload_disabled = true; | 921 | fi->fib_net->ipv4.fib_offload_disabled = true; |
403 | } | 922 | } |
404 | EXPORT_SYMBOL_GPL(netdev_switch_fib_ipv4_abort); | 923 | EXPORT_SYMBOL_GPL(switchdev_fib_ipv4_abort); |