diff options
Diffstat (limited to 'net/switchdev/switchdev.c')
| -rw-r--r-- | net/switchdev/switchdev.c | 98 |
1 files changed, 96 insertions, 2 deletions
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 81c4c0274b9b..99907d829419 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c | |||
| @@ -227,6 +227,65 @@ int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev, | |||
| 227 | } | 227 | } |
| 228 | EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink); | 228 | EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink); |
| 229 | 229 | ||
| 230 | static struct net_device *netdev_switch_get_lowest_dev(struct net_device *dev) | ||
| 231 | { | ||
| 232 | const struct net_device_ops *ops = dev->netdev_ops; | ||
| 233 | struct net_device *lower_dev; | ||
| 234 | struct net_device *port_dev; | ||
| 235 | struct list_head *iter; | ||
| 236 | |||
| 237 | /* Recusively search down until we find a sw port dev. | ||
| 238 | * (A sw port dev supports ndo_switch_parent_id_get). | ||
| 239 | */ | ||
| 240 | |||
| 241 | if (dev->features & NETIF_F_HW_SWITCH_OFFLOAD && | ||
| 242 | ops->ndo_switch_parent_id_get) | ||
| 243 | return dev; | ||
| 244 | |||
| 245 | netdev_for_each_lower_dev(dev, lower_dev, iter) { | ||
| 246 | port_dev = netdev_switch_get_lowest_dev(lower_dev); | ||
| 247 | if (port_dev) | ||
| 248 | return port_dev; | ||
| 249 | } | ||
| 250 | |||
| 251 | return NULL; | ||
| 252 | } | ||
| 253 | |||
| 254 | static struct net_device *netdev_switch_get_dev_by_nhs(struct fib_info *fi) | ||
| 255 | { | ||
| 256 | struct netdev_phys_item_id psid; | ||
| 257 | struct netdev_phys_item_id prev_psid; | ||
| 258 | struct net_device *dev = NULL; | ||
| 259 | int nhsel; | ||
| 260 | |||
| 261 | /* For this route, all nexthop devs must be on the same switch. */ | ||
| 262 | |||
| 263 | for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { | ||
| 264 | const struct fib_nh *nh = &fi->fib_nh[nhsel]; | ||
| 265 | |||
| 266 | if (!nh->nh_dev) | ||
| 267 | return NULL; | ||
| 268 | |||
| 269 | dev = netdev_switch_get_lowest_dev(nh->nh_dev); | ||
| 270 | if (!dev) | ||
| 271 | return NULL; | ||
| 272 | |||
| 273 | if (netdev_switch_parent_id_get(dev, &psid)) | ||
| 274 | return NULL; | ||
| 275 | |||
| 276 | if (nhsel > 0) { | ||
| 277 | if (prev_psid.id_len != psid.id_len) | ||
| 278 | return NULL; | ||
| 279 | if (memcmp(prev_psid.id, psid.id, psid.id_len)) | ||
| 280 | return NULL; | ||
| 281 | } | ||
| 282 | |||
| 283 | prev_psid = psid; | ||
| 284 | } | ||
| 285 | |||
| 286 | return dev; | ||
| 287 | } | ||
| 288 | |||
| 230 | /** | 289 | /** |
| 231 | * netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch | 290 | * netdev_switch_fib_ipv4_add - Add IPv4 route entry to switch |
| 232 | * | 291 | * |
| @@ -242,11 +301,27 @@ EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink); | |||
| 242 | int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, | 301 | int netdev_switch_fib_ipv4_add(u32 dst, int dst_len, struct fib_info *fi, |
| 243 | u8 tos, u8 type, u32 tb_id) | 302 | u8 tos, u8 type, u32 tb_id) |
| 244 | { | 303 | { |
| 304 | struct net_device *dev; | ||
| 305 | const struct net_device_ops *ops; | ||
| 306 | int err = 0; | ||
| 307 | |||
| 245 | /* Don't offload route if using custom ip rules */ | 308 | /* Don't offload route if using custom ip rules */ |
| 246 | if (fi->fib_net->ipv4.fib_has_custom_rules) | 309 | if (fi->fib_net->ipv4.fib_has_custom_rules) |
| 247 | return 0; | 310 | return 0; |
| 248 | 311 | ||
| 249 | return 0; | 312 | dev = netdev_switch_get_dev_by_nhs(fi); |
| 313 | if (!dev) | ||
| 314 | return 0; | ||
| 315 | ops = dev->netdev_ops; | ||
| 316 | |||
| 317 | if (ops->ndo_switch_fib_ipv4_add) { | ||
| 318 | err = ops->ndo_switch_fib_ipv4_add(dev, htonl(dst), dst_len, | ||
| 319 | fi, tos, type, tb_id); | ||
| 320 | if (!err) | ||
| 321 | fi->fib_flags |= RTNH_F_EXTERNAL; | ||
| 322 | } | ||
| 323 | |||
| 324 | return err; | ||
| 250 | } | 325 | } |
| 251 | EXPORT_SYMBOL(netdev_switch_fib_ipv4_add); | 326 | EXPORT_SYMBOL(netdev_switch_fib_ipv4_add); |
| 252 | 327 | ||
| @@ -265,6 +340,25 @@ EXPORT_SYMBOL(netdev_switch_fib_ipv4_add); | |||
| 265 | int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, | 340 | int netdev_switch_fib_ipv4_del(u32 dst, int dst_len, struct fib_info *fi, |
| 266 | u8 tos, u8 type, u32 tb_id) | 341 | u8 tos, u8 type, u32 tb_id) |
| 267 | { | 342 | { |
| 268 | return 0; | 343 | struct net_device *dev; |
| 344 | const struct net_device_ops *ops; | ||
| 345 | int err = 0; | ||
| 346 | |||
| 347 | if (!(fi->fib_flags & RTNH_F_EXTERNAL)) | ||
| 348 | return 0; | ||
| 349 | |||
| 350 | dev = netdev_switch_get_dev_by_nhs(fi); | ||
| 351 | if (!dev) | ||
| 352 | return 0; | ||
| 353 | ops = dev->netdev_ops; | ||
| 354 | |||
| 355 | if (ops->ndo_switch_fib_ipv4_del) { | ||
| 356 | err = ops->ndo_switch_fib_ipv4_del(dev, htonl(dst), dst_len, | ||
| 357 | fi, tos, type, tb_id); | ||
| 358 | if (!err) | ||
| 359 | fi->fib_flags &= ~RTNH_F_EXTERNAL; | ||
| 360 | } | ||
| 361 | |||
| 362 | return err; | ||
| 269 | } | 363 | } |
| 270 | EXPORT_SYMBOL(netdev_switch_fib_ipv4_del); | 364 | EXPORT_SYMBOL(netdev_switch_fib_ipv4_del); |
