Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bridge: per vlan dst_metadata netlink support

This patch adds support to attach per vlan tunnel info dst
metadata. This enables bridge driver to map vlan to tunnel_info
at ingress and egress. It uses the kernel dst_metadata infrastructure.

The initial use case is vlan to vni bridging, but the api is generic
to extend to any tunnel_info in the future:
- Uapi to configure/unconfigure/dump per vlan tunnel data
- netlink functions to configure vlan and tunnel_info mapping
- Introduces bridge port flag BR_LWT_VLAN to enable attach/detach
dst_metadata to bridged packets on ports. off by default.
- changes to existing code is mainly refactor some existing vlan
handling netlink code + hooks for new vlan tunnel code
- I have kept the vlan tunnel code isolated in separate files.
- most of the netlink vlan tunnel code is handling of vlan-tunid
ranges (follows the vlan range handling code). To conserve space
vlan-tunid by default are always dumped in ranges if applicable.

Use case:
example use for this is a vxlan bridging gateway or vtep
which maps vlans to vn-segments (or vnis).

iproute2 example (patched and pruned iproute2 output to just show
relevant fdb entries):
example shows same host mac learnt on two vni's and
vlan 100 maps to vni 1000, vlan 101 maps to vni 1001

before (netdev per vni):
$bridge fdb show | grep "00:02:00:00:00:03"
00:02:00:00:00:03 dev vxlan1001 vlan 101 master bridge
00:02:00:00:00:03 dev vxlan1001 dst 12.0.0.8 self
00:02:00:00:00:03 dev vxlan1000 vlan 100 master bridge
00:02:00:00:00:03 dev vxlan1000 dst 12.0.0.8 self

after this patch with collect metdata in bridged mode (single netdev):
$bridge fdb show | grep "00:02:00:00:00:03"
00:02:00:00:00:03 dev vxlan0 vlan 101 master bridge
00:02:00:00:00:03 dev vxlan0 src_vni 1001 dst 12.0.0.8 self
00:02:00:00:00:03 dev vxlan0 vlan 100 master bridge
00:02:00:00:00:03 dev vxlan0 src_vni 1000 dst 12.0.0.8 self

CC: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Roopa Prabhu and committed by
David S. Miller
efa5356b b3c7ef0a

+644 -51
+3 -2
net/bridge/Makefile
··· 6 6 7 7 bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ 8 8 br_ioctl.o br_stp.o br_stp_bpdu.o \ 9 - br_stp_if.o br_stp_timer.o br_netlink.o 9 + br_stp_if.o br_stp_timer.o br_netlink.o \ 10 + br_netlink_tunnel.o 10 11 11 12 bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o 12 13 ··· 19 18 20 19 bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o 21 20 22 - bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o 21 + bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o br_vlan_tunnel.o 23 22 24 23 bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o 25 24
+98 -48
net/bridge/br_netlink.c
··· 20 20 21 21 #include "br_private.h" 22 22 #include "br_private_stp.h" 23 + #include "br_private_tunnel.h" 23 24 24 25 static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg, 25 26 u32 filter_mask) ··· 96 95 u32 filter_mask) 97 96 { 98 97 struct net_bridge_vlan_group *vg = NULL; 99 - struct net_bridge_port *p; 98 + struct net_bridge_port *p = NULL; 100 99 struct net_bridge *br; 101 100 int num_vlan_infos; 101 + size_t vinfo_sz = 0; 102 102 103 103 rcu_read_lock(); 104 104 if (br_port_exists(dev)) { ··· 112 110 num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask); 113 111 rcu_read_unlock(); 114 112 113 + if (p && (p->flags & BR_VLAN_TUNNEL)) 114 + vinfo_sz += br_get_vlan_tunnel_info_size(vg); 115 + 115 116 /* Each VLAN is returned in bridge_vlan_info along with flags */ 116 - return num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info)); 117 + vinfo_sz += num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info)); 118 + 119 + return vinfo_sz; 117 120 } 118 121 119 122 static inline size_t br_port_info_size(void) ··· 135 128 + nla_total_size(1) /* IFLA_BRPORT_UNICAST_FLOOD */ 136 129 + nla_total_size(1) /* IFLA_BRPORT_PROXYARP */ 137 130 + nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */ 131 + + nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */ 138 132 + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ 139 133 + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ 140 134 + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ ··· 202 194 nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) || 203 195 nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, 204 196 p->topology_change_ack) || 205 - nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending)) 197 + nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) || 198 + nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags & 199 + BR_VLAN_TUNNEL))) 206 200 return -EMSGSIZE; 207 201 208 202 timerval = br_timer_value(&p->message_age_timer); ··· 427 417 err = br_fill_ifvlaninfo_compressed(skb, vg); 428 418 else 429 419 err = br_fill_ifvlaninfo(skb, vg); 420 + 421 + if (port && (port->flags & BR_VLAN_TUNNEL)) 422 + err = br_fill_vlan_tunnel_info(skb, vg); 430 423 rcu_read_unlock(); 431 424 if (err) 432 425 goto nla_put_failure; ··· 530 517 return err; 531 518 } 532 519 520 + static int br_process_vlan_info(struct net_bridge *br, 521 + struct net_bridge_port *p, int cmd, 522 + struct bridge_vlan_info *vinfo_curr, 523 + struct bridge_vlan_info **vinfo_last) 524 + { 525 + if (!vinfo_curr->vid || vinfo_curr->vid >= VLAN_VID_MASK) 526 + return -EINVAL; 527 + 528 + if (vinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { 529 + /* check if we are already processing a range */ 530 + if (*vinfo_last) 531 + return -EINVAL; 532 + *vinfo_last = vinfo_curr; 533 + /* don't allow range of pvids */ 534 + if ((*vinfo_last)->flags & BRIDGE_VLAN_INFO_PVID) 535 + return -EINVAL; 536 + return 0; 537 + } 538 + 539 + if (*vinfo_last) { 540 + struct bridge_vlan_info tmp_vinfo; 541 + int v, err; 542 + 543 + if (!(vinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END)) 544 + return -EINVAL; 545 + 546 + if (vinfo_curr->vid <= (*vinfo_last)->vid) 547 + return -EINVAL; 548 + 549 + memcpy(&tmp_vinfo, *vinfo_last, 550 + sizeof(struct bridge_vlan_info)); 551 + for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) { 552 + tmp_vinfo.vid = v; 553 + err = br_vlan_info(br, p, cmd, &tmp_vinfo); 554 + if (err) 555 + break; 556 + } 557 + *vinfo_last = NULL; 558 + 559 + return 0; 560 + } 561 + 562 + return br_vlan_info(br, p, cmd, vinfo_curr); 563 + } 564 + 533 565 static int br_afspec(struct net_bridge *br, 534 566 struct net_bridge_port *p, 535 567 struct nlattr *af_spec, 536 568 int cmd) 537 569 { 538 - struct bridge_vlan_info *vinfo_start = NULL; 539 - struct bridge_vlan_info *vinfo = NULL; 570 + struct bridge_vlan_info *vinfo_curr = NULL; 571 + struct bridge_vlan_info *vinfo_last = NULL; 540 572 struct nlattr *attr; 541 - int err = 0; 542 - int rem; 573 + struct vtunnel_info tinfo_last = {}; 574 + struct vtunnel_info tinfo_curr = {}; 575 + int err = 0, rem; 543 576 544 577 nla_for_each_nested(attr, af_spec, rem) { 545 - if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) 546 - continue; 547 - if (nla_len(attr) != sizeof(struct bridge_vlan_info)) 548 - return -EINVAL; 549 - vinfo = nla_data(attr); 550 - if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK) 551 - return -EINVAL; 552 - if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { 553 - if (vinfo_start) 578 + err = 0; 579 + switch (nla_type(attr)) { 580 + case IFLA_BRIDGE_VLAN_TUNNEL_INFO: 581 + if (!(p->flags & BR_VLAN_TUNNEL)) 554 582 return -EINVAL; 555 - vinfo_start = vinfo; 556 - /* don't allow range of pvids */ 557 - if (vinfo_start->flags & BRIDGE_VLAN_INFO_PVID) 558 - return -EINVAL; 559 - continue; 560 - } 561 - 562 - if (vinfo_start) { 563 - struct bridge_vlan_info tmp_vinfo; 564 - int v; 565 - 566 - if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) 567 - return -EINVAL; 568 - 569 - if (vinfo->vid <= vinfo_start->vid) 570 - return -EINVAL; 571 - 572 - memcpy(&tmp_vinfo, vinfo_start, 573 - sizeof(struct bridge_vlan_info)); 574 - 575 - for (v = vinfo_start->vid; v <= vinfo->vid; v++) { 576 - tmp_vinfo.vid = v; 577 - err = br_vlan_info(br, p, cmd, &tmp_vinfo); 578 - if (err) 579 - break; 580 - } 581 - vinfo_start = NULL; 582 - } else { 583 - err = br_vlan_info(br, p, cmd, vinfo); 584 - } 585 - if (err) 583 + err = br_parse_vlan_tunnel_info(attr, &tinfo_curr); 584 + if (err) 585 + return err; 586 + err = br_process_vlan_tunnel_info(br, p, cmd, 587 + &tinfo_curr, 588 + &tinfo_last); 589 + if (err) 590 + return err; 586 591 break; 592 + case IFLA_BRIDGE_VLAN_INFO: 593 + if (nla_len(attr) != sizeof(struct bridge_vlan_info)) 594 + return -EINVAL; 595 + vinfo_curr = nla_data(attr); 596 + err = br_process_vlan_info(br, p, cmd, vinfo_curr, 597 + &vinfo_last); 598 + if (err) 599 + return err; 600 + break; 601 + } 602 + 603 + if (err) 604 + return err; 587 605 } 588 606 589 607 return err; ··· 674 630 /* Process bridge protocol info on port */ 675 631 static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) 676 632 { 677 - int err; 678 633 unsigned long old_flags = p->flags; 634 + bool br_vlan_tunnel_old = false; 635 + int err; 679 636 680 637 br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); 681 638 br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); ··· 688 643 br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST); 689 644 br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); 690 645 br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI); 646 + 647 + br_vlan_tunnel_old = (p->flags & BR_VLAN_TUNNEL) ? true : false; 648 + br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL); 649 + if (br_vlan_tunnel_old && !(p->flags & BR_VLAN_TUNNEL)) 650 + nbp_vlan_tunnel_info_flush(p); 691 651 692 652 if (tb[IFLA_BRPORT_COST]) { 693 653 err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
+10
net/bridge/br_private.h
··· 91 91 struct u64_stats_sync syncp; 92 92 }; 93 93 94 + struct br_tunnel_info { 95 + __be64 tunnel_id; 96 + struct metadata_dst *tunnel_dst; 97 + }; 98 + 94 99 /** 95 100 * struct net_bridge_vlan - per-vlan entry 96 101 * ··· 118 113 */ 119 114 struct net_bridge_vlan { 120 115 struct rhash_head vnode; 116 + struct rhash_head tnode; 121 117 u16 vid; 122 118 u16 flags; 123 119 struct br_vlan_stats __percpu *stats; ··· 130 124 atomic_t refcnt; 131 125 struct net_bridge_vlan *brvlan; 132 126 }; 127 + 128 + struct br_tunnel_info tinfo; 129 + 133 130 struct list_head vlist; 134 131 135 132 struct rcu_head rcu; ··· 154 145 */ 155 146 struct net_bridge_vlan_group { 156 147 struct rhashtable vlan_hash; 148 + struct rhashtable tunnel_hash; 157 149 struct list_head vlan_list; 158 150 u16 num_vlans; 159 151 u16 pvid;
+72
net/bridge/br_private_tunnel.h
··· 1 + /* 2 + * Bridge per vlan tunnels 3 + * 4 + * Authors: 5 + * Roopa Prabhu <roopa@cumulusnetworks.com> 6 + * 7 + * This program is free software; you can redistribute it and/or 8 + * modify it under the terms of the GNU General Public License 9 + * as published by the Free Software Foundation; either version 10 + * 2 of the License, or (at your option) any later version. 11 + */ 12 + 13 + #ifndef _BR_PRIVATE_TUNNEL_H 14 + #define _BR_PRIVATE_TUNNEL_H 15 + 16 + struct vtunnel_info { 17 + u32 tunid; 18 + u16 vid; 19 + u16 flags; 20 + }; 21 + 22 + /* br_netlink_tunnel.c */ 23 + int br_parse_vlan_tunnel_info(struct nlattr *attr, 24 + struct vtunnel_info *tinfo); 25 + int br_process_vlan_tunnel_info(struct net_bridge *br, 26 + struct net_bridge_port *p, 27 + int cmd, 28 + struct vtunnel_info *tinfo_curr, 29 + struct vtunnel_info *tinfo_last); 30 + int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg); 31 + int br_fill_vlan_tunnel_info(struct sk_buff *skb, 32 + struct net_bridge_vlan_group *vg); 33 + 34 + #ifdef CONFIG_BRIDGE_VLAN_FILTERING 35 + /* br_vlan_tunnel.c */ 36 + int vlan_tunnel_init(struct net_bridge_vlan_group *vg); 37 + void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg); 38 + int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid); 39 + int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id); 40 + void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port); 41 + void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg, 42 + struct net_bridge_vlan *vlan); 43 + #else 44 + static inline int vlan_tunnel_init(struct net_bridge_vlan_group *vg) 45 + { 46 + return 0; 47 + } 48 + 49 + static inline int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, 50 + u16 vid) 51 + { 52 + return 0; 53 + } 54 + 55 + static inline int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, 56 + u16 vid, u32 tun_id) 57 + { 58 + return 0; 59 + } 60 + 61 + static inline void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port) 62 + { 63 + } 64 + 65 + static inline void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg, 66 + struct net_bridge_vlan *vlan) 67 + { 68 + } 69 + 70 + #endif 71 + 72 + #endif
+16 -1
net/bridge/br_vlan.c
··· 5 5 #include <net/switchdev.h> 6 6 7 7 #include "br_private.h" 8 + #include "br_private_tunnel.h" 8 9 9 10 static inline int br_vlan_cmp(struct rhashtable_compare_arg *arg, 10 11 const void *ptr) ··· 311 310 } 312 311 313 312 if (masterv != v) { 313 + vlan_tunnel_info_del(vg, v); 314 314 rhashtable_remove_fast(&vg->vlan_hash, &v->vnode, 315 315 br_vlan_rht_params); 316 316 __vlan_del_list(v); ··· 327 325 { 328 326 WARN_ON(!list_empty(&vg->vlan_list)); 329 327 rhashtable_destroy(&vg->vlan_hash); 328 + vlan_tunnel_deinit(vg); 330 329 kfree(vg); 331 330 } 332 331 ··· 615 612 616 613 br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid); 617 614 br_fdb_delete_by_port(br, NULL, vid, 0); 615 + 616 + vlan_tunnel_info_del(vg, v); 618 617 619 618 return __vlan_del(v); 620 619 } ··· 923 918 ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params); 924 919 if (ret) 925 920 goto err_rhtbl; 921 + ret = vlan_tunnel_init(vg); 922 + if (ret) 923 + goto err_tunnel_init; 926 924 INIT_LIST_HEAD(&vg->vlan_list); 927 925 br->vlan_proto = htons(ETH_P_8021Q); 928 926 br->default_pvid = 1; ··· 940 932 return ret; 941 933 942 934 err_vlan_add: 935 + vlan_tunnel_deinit(vg); 936 + err_tunnel_init: 943 937 rhashtable_destroy(&vg->vlan_hash); 944 938 err_rhtbl: 945 939 kfree(vg); ··· 971 961 ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params); 972 962 if (ret) 973 963 goto err_rhtbl; 964 + ret = vlan_tunnel_init(vg); 965 + if (ret) 966 + goto err_tunnel_init; 974 967 INIT_LIST_HEAD(&vg->vlan_list); 975 968 rcu_assign_pointer(p->vlgrp, vg); 976 969 if (p->br->default_pvid) { ··· 989 976 err_vlan_add: 990 977 RCU_INIT_POINTER(p->vlgrp, NULL); 991 978 synchronize_rcu(); 992 - rhashtable_destroy(&vg->vlan_hash); 979 + vlan_tunnel_deinit(vg); 993 980 err_vlan_enabled: 981 + err_tunnel_init: 982 + rhashtable_destroy(&vg->vlan_hash); 994 983 err_rhtbl: 995 984 kfree(vg); 996 985
+149
net/bridge/br_vlan_tunnel.c
··· 1 + /* 2 + * Bridge per vlan tunnel port dst_metadata handling code 3 + * 4 + * Authors: 5 + * Roopa Prabhu <roopa@cumulusnetworks.com> 6 + * 7 + * This program is free software; you can redistribute it and/or 8 + * modify it under the terms of the GNU General Public License 9 + * as published by the Free Software Foundation; either version 10 + * 2 of the License, or (at your option) any later version. 11 + */ 12 + 13 + #include <linux/kernel.h> 14 + #include <linux/netdevice.h> 15 + #include <linux/rtnetlink.h> 16 + #include <linux/slab.h> 17 + #include <net/switchdev.h> 18 + #include <net/dst_metadata.h> 19 + 20 + #include "br_private.h" 21 + #include "br_private_tunnel.h" 22 + 23 + static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg, 24 + const void *ptr) 25 + { 26 + const struct net_bridge_vlan *vle = ptr; 27 + __be64 tunid = *(__be64 *)arg->key; 28 + 29 + return vle->tinfo.tunnel_id != tunid; 30 + } 31 + 32 + static const struct rhashtable_params br_vlan_tunnel_rht_params = { 33 + .head_offset = offsetof(struct net_bridge_vlan, tnode), 34 + .key_offset = offsetof(struct net_bridge_vlan, tinfo.tunnel_id), 35 + .key_len = sizeof(__be64), 36 + .nelem_hint = 3, 37 + .locks_mul = 1, 38 + .obj_cmpfn = br_vlan_tunid_cmp, 39 + .automatic_shrinking = true, 40 + }; 41 + 42 + void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg, 43 + struct net_bridge_vlan *vlan) 44 + { 45 + if (!vlan->tinfo.tunnel_dst) 46 + return; 47 + rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode, 48 + br_vlan_tunnel_rht_params); 49 + vlan->tinfo.tunnel_id = 0; 50 + dst_release(&vlan->tinfo.tunnel_dst->dst); 51 + vlan->tinfo.tunnel_dst = NULL; 52 + } 53 + 54 + static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg, 55 + struct net_bridge_vlan *vlan, u32 tun_id) 56 + { 57 + struct metadata_dst *metadata = NULL; 58 + __be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id)); 59 + int err; 60 + 61 + if (vlan->tinfo.tunnel_dst) 62 + return -EEXIST; 63 + 64 + metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY, 65 + key, 0); 66 + if (!metadata) 67 + return -EINVAL; 68 + 69 + metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE; 70 + vlan->tinfo.tunnel_dst = metadata; 71 + vlan->tinfo.tunnel_id = key; 72 + 73 + err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode, 74 + br_vlan_tunnel_rht_params); 75 + if (err) 76 + goto out; 77 + 78 + return 0; 79 + out: 80 + dst_release(&vlan->tinfo.tunnel_dst->dst); 81 + 82 + return err; 83 + } 84 + 85 + /* Must be protected by RTNL. 86 + * Must be called with vid in range from 1 to 4094 inclusive. 87 + */ 88 + int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id) 89 + { 90 + struct net_bridge_vlan_group *vg; 91 + struct net_bridge_vlan *vlan; 92 + 93 + ASSERT_RTNL(); 94 + 95 + vg = nbp_vlan_group(port); 96 + vlan = br_vlan_find(vg, vid); 97 + if (!vlan) 98 + return -EINVAL; 99 + 100 + return __vlan_tunnel_info_add(vg, vlan, tun_id); 101 + } 102 + 103 + /* Must be protected by RTNL. 104 + * Must be called with vid in range from 1 to 4094 inclusive. 105 + */ 106 + int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid) 107 + { 108 + struct net_bridge_vlan_group *vg; 109 + struct net_bridge_vlan *v; 110 + 111 + ASSERT_RTNL(); 112 + 113 + vg = nbp_vlan_group(port); 114 + v = br_vlan_find(vg, vid); 115 + if (!v) 116 + return -ENOENT; 117 + 118 + vlan_tunnel_info_del(vg, v); 119 + 120 + return 0; 121 + } 122 + 123 + static void __vlan_tunnel_info_flush(struct net_bridge_vlan_group *vg) 124 + { 125 + struct net_bridge_vlan *vlan, *tmp; 126 + 127 + list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) 128 + vlan_tunnel_info_del(vg, vlan); 129 + } 130 + 131 + void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port) 132 + { 133 + struct net_bridge_vlan_group *vg; 134 + 135 + ASSERT_RTNL(); 136 + 137 + vg = nbp_vlan_group(port); 138 + __vlan_tunnel_info_flush(vg); 139 + } 140 + 141 + int vlan_tunnel_init(struct net_bridge_vlan_group *vg) 142 + { 143 + return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params); 144 + } 145 + 146 + void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg) 147 + { 148 + rhashtable_destroy(&vg->tunnel_hash); 149 + }