// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) Intel Corporation * Author: Joby Thampan */ #include #include #include #include "../datapath.h" #include "../datapath_instance.h" #include #include <../net/bridge/br_private.h> #define MAX_FID 64 /* Only 6 bits for FiD */ struct br_vlan_cap { u32 maxvlanid; u32 maxbridgeid; }; struct br_vlan_cap g_br_vlan_cap = { 0 }; enum BR_VLAN_FLAG { BR_VLAN_LIST_TOP_ENTRY = BIT(0), BR_VLAN_DEREGISTER = BIT(1) }; static int dp_del_vlan_entry(struct br_info *br_info, struct vlist_entry *vlist_entry, u16 bport, u16 vlanid, int flags); /* Check Bridge have the entry added through Self * if vlanid=0, returns first entry * if valid vlanid, returns entry pointed by VLAN ID */ struct vlist_entry *get_vlist_entry_in_br(struct br_info *br_info, u16 vlanid, int flags) { struct vlist_entry *list_entry = NULL; /* Returning the top entry in list or Return the exact matching Entry */ if (flags & BR_VLAN_LIST_TOP_ENTRY) { list_for_each_entry (list_entry, &br_info->br_vlan_list, list) { if (!list_entry->vlan_entry) continue; return list_entry; } } else { list_for_each_entry (list_entry, &br_info->br_vlan_list, list) { if (!list_entry->vlan_entry || (vlanid != list_entry->vlan_entry->vlan_id)) continue; return list_entry; } } return NULL; } /* Check through all Bridge Member port have the entry added through Master * if have return the entry */ struct vlist_entry *get_vlist_entry_in_all_bp(struct br_info *br_info, u16 vlanid) { struct vlist_entry *list_entry = NULL; struct bridge_member_port *bmp; /* Check Bridge Member port have the entry added through Master */ list_for_each_entry (bmp, &br_info->bp_list, list) { list_for_each_entry (list_entry, &bmp->bport_vlan_list, list) { if (!list_entry->vlan_entry || (vlanid != list_entry->vlan_entry->vlan_id)) continue; return list_entry; } } return NULL; } /* Get the List Head for the BP Vlan List */ struct list_head *get_vlan_entry_head_in_bp(struct br_info *br_info, int bport) { struct bridge_member_port *bmp; list_for_each_entry (bmp, &br_info->bp_list, list) { if (bmp->bportid != bport) continue; return &bmp->bport_vlan_list; } return NULL; } /* Check through Bridge Member port specified have the entry added * through Master if have return the entry */ struct vlist_entry *get_vlist_entry_in_bp(struct br_info *br_info, int bport, u16 vlanid, int flags) { struct vlist_entry *list_entry = NULL; struct bridge_member_port *bmp; /* Returning the top entry in list or Return the exact matching Entry */ if (flags & BR_VLAN_LIST_TOP_ENTRY) { list_for_each_entry (bmp, &br_info->bp_list, list) { if (bmp->bportid != bport) continue; list_for_each_entry (list_entry, &bmp->bport_vlan_list, list) { if (!list_entry->vlan_entry) continue; return list_entry; } } } else { list_for_each_entry (bmp, &br_info->bp_list, list) { if (bmp->bportid != bport) continue; list_for_each_entry (list_entry, &bmp->bport_vlan_list, list) { if (!list_entry->vlan_entry || (vlanid != list_entry->vlan_entry->vlan_id)) continue; return list_entry; } } } return NULL; } /* When Bridge Entry is created/deleted in DPM we check whether this bridge is * VLAN Aware and change ndo_bridge ops */ int dp_register_br_vlan(struct br_info *br_info, int flags) { struct net_bridge *br; int offset; int flag = DP_OPS_NETDEV | flags; struct net_device *br_dev; struct vlist_entry *list_entry = NULL; if (!br_info) return DP_FAILURE; /* TODO: This cap should be per bridge in Br_Info */ if (!g_br_vlan_cap.maxbridgeid && !g_br_vlan_cap.maxvlanid) return DP_FAILURE; br_dev = br_info->dev; if (!netif_is_bridge_master(br_dev)) { pr_err("%s: dev %s is not bridge dev\n", __func__, br_dev->name); return DP_FAILURE; } /* Netdev ops need to register only if VLAN Aware */ br = netdev_priv(br_dev); if (!br_vlan_enabled(br)) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Bridge %s is not VLAN Aware\n", __func__, br_dev->name); return DP_SUCCESS; } /* Using br_vlan_en flag to check bridge ops registered */ if (flags & DP_OPS_RESET) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Un-Register br %s netdev ops\n", __func__, br_dev->name); br_info->br_vlan_en = false; /* Mark as ops unreg */ while ((list_entry = get_vlist_entry_in_br(br_info, 0, BR_VLAN_LIST_TOP_ENTRY))) { dp_del_vlan_entry(br_info, list_entry, 0, list_entry->vlan_entry->vlan_id, BRIDGE_FLAGS_MASTER); } } else if (br_info->br_vlan_en) { goto EXIT; /* Already ops registered for this bridge */ } else { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Register br %s netdev ops\n", __func__, br_dev->name); br_info->br_vlan_en = true; /* mark as ops regd */ } offset = offsetof(const struct net_device_ops, ndo_bridge_setlink); dp_set_net_dev_ops_priv(br_dev, &dp_ndo_bridge_setlink, offset, flag); offset = offsetof(const struct net_device_ops, ndo_bridge_dellink); dp_set_net_dev_ops_priv(br_dev, &dp_ndo_bridge_dellink, offset, flag); EXIT: return DP_SUCCESS; } /* When Bridge Port Entry is created/deleted in DPM * we check whether this bridge is VLAN Aware and change ndo_bridge ops */ int dp_register_bport_vlan(struct br_info *br_info, struct net_device *dev, int bport, int flags) { int offset; int flag = DP_OPS_NETDEV | flags; struct vlist_entry *list_entry = NULL; if (!dev || !br_info) return DP_FAILURE; /* TODO: This cap should be per bridge in Br_Info */ if (!g_br_vlan_cap.maxbridgeid && !g_br_vlan_cap.maxvlanid) return DP_FAILURE; if (!netif_is_bridge_port(dev)) { pr_err("%s: dev %s is not bridge port\n", __func__, dev->name); return DP_FAILURE; } /* If not VLAN Aware we are not registering ops for this device */ if (!br_info->br_vlan_en) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Vlan Aware is not enabled for this Bridge %s\n", __func__, br_info->dev->name); return DP_SUCCESS; } if (flags & DP_OPS_RESET) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Un-Register dev=%s in br=%s netdev ops\n", __func__, dev->name, br_info->dev->name); while ((list_entry = get_vlist_entry_in_bp(br_info, bport, 0, BR_VLAN_LIST_TOP_ENTRY))) { dp_del_vlan_entry(br_info, list_entry, bport, list_entry->vlan_entry->vlan_id, BRIDGE_FLAGS_MASTER); } } else { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Register dev=%s in br=%s netdev ops\n", __func__, dev->name, br_info->dev->name); } /* Register member port ndo_bridge ops callback */ offset = offsetof(const struct net_device_ops, ndo_bridge_setlink); dp_set_net_dev_ops_priv(dev, &dp_ndo_bridge_setlink, offset, flag); offset = offsetof(const struct net_device_ops, ndo_bridge_dellink); dp_set_net_dev_ops_priv(dev, &dp_ndo_bridge_dellink, offset, flag); return DP_SUCCESS; } /* Copy the Bridge Settings of bridge to the new Vlan Aware FiD's */ static int dp_gsw_bridge_cfg_copy(int inst, u16 src_fid, u16 dst_fid) { GSW_return_t ret = DP_SUCCESS; GSW_BRIDGE_config_t brcfg; struct core_ops *gsw_handle; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: src_fid=%d dst_fid=%d\n", __func__, src_fid, dst_fid); gsw_handle = dp_port_prop[inst].ops[0]; memset(&brcfg, 0, sizeof(brcfg)); brcfg.nBridgeId = src_fid; ret = gsw_handle->gsw_brdg_ops.Bridge_ConfigGet(gsw_handle, &brcfg); if (ret) return ret; brcfg.nBridgeId = dst_fid; ret = gsw_handle->gsw_brdg_ops.Bridge_ConfigSet(gsw_handle, &brcfg); if (ret) return ret; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "FID(%d to %d) copy success\n", src_fid, dst_fid); return ret; } /* Free the Bridge FiD created in GSWIP */ static int deallocate_br_vlan_fid(struct br_info *br_info, u16 vlanid, u16 fid) { struct inst_info *dp_info = get_dp_prop_info(0); int ret = 0; ret = dp_info->swdev_free_brcfg(0, fid); if (ret) { pr_err("%s: swdev_free_brcfg Failed for FiD %d\n", __func__, fid); return DP_FAILURE; } br_info->num_fid--; return DP_SUCCESS; } /* Allocate new VLAN Aware Bridge FiD in GSWIP */ static int allocate_br_vlan_fid(struct br_info *br_info, u16 vlanid, int bport) { u16 fid = 0; struct inst_info *dp_info = get_dp_prop_info(0); int new_brid; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Br=%s Br_Fid=%d bport=%d vlanid=%d num_vlan=%d num_fid=%d\n", __func__, br_info->dev->name, br_info->fid, bport, vlanid, br_info->num_vlan, br_info->num_fid); if (br_info->num_vlan > g_br_vlan_cap.maxvlanid) { pr_err("%s: Cannot Support more than %d VLAN ID's \n", __func__, g_br_vlan_cap.maxvlanid); return DP_FAILURE; } if (br_info->num_fid > g_br_vlan_cap.maxbridgeid) { pr_err("%s: Cannot Support more than %d Bridge ID's \n", __func__, g_br_vlan_cap.maxbridgeid); return DP_FAILURE; } new_brid = dp_info->swdev_alloc_bridge_id(br_info->inst); if (new_brid <= 0) { pr_err("%s: Switch bridge alloc failed\n", __func__); return DP_FAILURE; } fid = new_brid; if (new_brid > MAX_FID) { dp_info->swdev_free_brcfg(br_info->inst, fid); pr_err("%s: VLAN Aware support max 64 FiD (VlanID + BriID)\n", __func__); return DP_FAILURE; } if (dp_gsw_bridge_cfg_copy(br_info->inst, br_info->fid, fid)) { dp_info->swdev_free_brcfg(br_info->inst, fid); pr_err("%s: Bridge Cfg Copy for Fid %d to Fid %d failed\n", __func__, br_info->fid, fid); return DP_FAILURE; } br_info->num_fid++; return fid; } /* Add a VLAN Entry, If already have in BP or Bridge, reuse the VLAN entry * In 1 Bridge Each VLAN have only 1 uniqueue FiD * NewFID = VlanID + BridgeID */ static struct vlan_entry *dp_add_vlan_entry(struct br_info *br_info, struct vlist_entry *br_ventry, struct vlist_entry *bp_ventry, int bport, u16 vlanid, int flags) { struct vlist_entry *vlist_entry; struct vlan_entry *ventry; struct list_head *vhead; int fid = 0; bool alloc_fid = false; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: dev=%s bport=%d vlanid=%d flags=%08x\n", __func__, br_info->dev->name, bport, vlanid, flags); if (br_ventry && bp_ventry) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: No Need to add both br & bport have the entry\n", __func__); return DP_SUCCESS; } vlist_entry = devm_kzalloc(&g_dp_dev->dev, sizeof(struct vlist_entry), GFP_ATOMIC); if (!vlist_entry) return NULL; /* if bridge or bridge member port have the VLAN Entry * Add to the corresponding list increase ref_cnt and return Entry */ if (flags & BRIDGE_FLAGS_SELF) vhead = &br_info->br_vlan_list; else vhead = get_vlan_entry_head_in_bp(br_info, bport); if (!vhead) { devm_kfree(&g_dp_dev->dev, vlist_entry); return NULL; } /* Bridge Port or Bridge dont have the VLAN Entry allocate a new one */ if (!br_ventry && !bp_ventry) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Allocating a new entry\n", __func__); ventry = devm_kzalloc(&g_dp_dev->dev, sizeof(struct vlan_entry), GFP_ATOMIC); if (!ventry) { devm_kfree(&g_dp_dev->dev, vlist_entry); return NULL; } vlist_entry->vlan_entry = ventry; vlist_entry->vlan_entry->vlan_id = vlanid; vlist_entry->vlan_entry->ref_cnt++; list_add(&vlist_entry->list, vhead); return vlist_entry->vlan_entry; } else if (br_ventry && !bp_ventry) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Allocating a br entry\n", __func__); vlist_entry->vlan_entry = br_ventry->vlan_entry; alloc_fid = true; /* BR already have, now going to create BP */ } else if (!br_ventry && bp_ventry) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Allocating a bp entry\n", __func__); vlist_entry->vlan_entry = bp_ventry->vlan_entry; /* BP already have, now we are going to create BR */ if (flags & BRIDGE_FLAGS_SELF) alloc_fid = true; } else { pr_err("%s: Impossible alloc both BP and BR have VLAN Entry\n", __func__); devm_kfree(&g_dp_dev->dev, vlist_entry); return NULL; } /* FiD is created only when 1 Self + 1 Master */ if (alloc_fid) { /* Allocate a unique FID for this VLAN ID */ fid = allocate_br_vlan_fid(br_info, vlanid, bport); if (fid < 0) { devm_kfree(&g_dp_dev->dev, vlist_entry); return NULL; } vlist_entry->vlan_entry->fid = fid; } vlist_entry->vlan_entry->ref_cnt++; list_add(&vlist_entry->list, vhead); return vlist_entry->vlan_entry; } /* Deleting a bridge member VLAN Entry and FID */ static int dp_del_vlan_entry(struct br_info *br_info, struct vlist_entry *vlist_entry, u16 bport, u16 vlanid, int flags) { struct vlan_entry *ventry; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Br=%s bport=%d vlanid=%d flags=%08x\n", __func__, br_info->dev->name, bport, vlanid, flags); /* if Entry or Ref Count is not present this VLAN ID doesn't exist */ if (!vlist_entry || !vlist_entry->vlan_entry || !vlist_entry->vlan_entry->ref_cnt) { pr_err("%s: VLAN ID %d is not present\n", __func__, vlanid); return DP_SUCCESS; } ventry = vlist_entry->vlan_entry; /* if usage > 1, Cannot delete now!! decrease ref count and delete the * entry from list of either br or bridge member */ if (ventry->ref_cnt > 1) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "Deleting entry in Br=%s Bp=%d vlanid=%d ref_cnt=%d\n", br_info->dev->name, bport, ventry->vlan_id, ventry->ref_cnt); ventry->ref_cnt--; list_del(&vlist_entry->list); devm_kfree(&g_dp_dev->dev, vlist_entry); return DP_SUCCESS; } DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Fid=%d Br=%s BrFid=%d vlanid=%d bport=%d ref_cnt=%d\n", __func__, ventry->fid, br_info->dev->name, br_info->fid, vlanid, bport, ventry->ref_cnt); /* Deallocate VLAN aware FiD, if FiD is > 0 */ if (ventry->fid) deallocate_br_vlan_fid(br_info, vlanid, ventry->fid); /* Decrease ref count and delete the VLAN ID Entry */ ventry->ref_cnt--; list_del(&vlist_entry->list); devm_kfree(&g_dp_dev->dev, ventry); devm_kfree(&g_dp_dev->dev, vlist_entry); br_info->num_vlan--; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "Vlan ID %d removed on bridge %s\n", vlanid, br_info->dev->name); return DP_SUCCESS; } /* Self is called for bridge interface to assign VLAN ID */ static int dp_register_self(struct br_info *br_info, u16 vlanid, int flags) { int ret = DP_FAILURE; struct vlan_entry *ventry; struct vlist_entry *vlist_entry, *br_ventry, *bp_ventry; /* if not bridge return error */ if (!netif_is_bridge_master(br_info->dev)) return ret; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: dev=%s vlanid=%d flags=%08x\n", __func__, br_info->dev->name, vlanid, flags); if (flags & BR_VLAN_DEREGISTER) { vlist_entry = get_vlist_entry_in_br(br_info, vlanid, 0); ret = dp_del_vlan_entry(br_info, vlist_entry, 0, vlanid, BRIDGE_FLAGS_SELF); goto EXIT; } /* Dont allow multiple commands to add multiple entries in list */ br_ventry = get_vlist_entry_in_br(br_info, vlanid, 0); if (br_ventry) { pr_info("%s: Vlan ID %d is already present in Br %s\n", __func__, vlanid, br_info->dev->name); goto EXIT; } bp_ventry = get_vlist_entry_in_all_bp(br_info, vlanid); /* if not present will allocate a new one */ ventry = dp_add_vlan_entry(br_info, br_ventry, bp_ventry, 0, vlanid, BRIDGE_FLAGS_SELF); if (!ventry) { pr_err("%s: Failed to Add vland id %d to br %s\n", __func__, vlanid, br_info->dev->name); goto EXIT; } DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "VLAN Id %d added for bridge %s Fid %d ref_cnt %d\n", ventry->vlan_id, br_info->dev->name, ventry->fid, ventry->ref_cnt); br_info->num_vlan++; ret = DP_SUCCESS; EXIT: return ret; } /* This API will be called for Bridge Member ports */ static int dp_register_master(struct br_info *br_info, struct net_device *dev, u16 vlanid, int flags) { dp_subif_t subif; struct vlan_entry *ventry; int ret = DP_FAILURE; struct vlist_entry *vlist_entry, *br_ventry, *bp_ventry; if (dp_get_netif_subifid(dev, NULL, NULL, NULL, &subif, 0)) { pr_err("%s: dp_get_netif_subifid failed\n", __func__); return DP_FAILURE; } DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Br=%s bport=%d vlanid=%d flags=%08x\n", __func__, br_info->dev->name, subif.bport, vlanid, flags); if (flags & BR_VLAN_DEREGISTER) { vlist_entry = get_vlist_entry_in_bp(br_info, subif.bport, vlanid, 0); ret = dp_del_vlan_entry(br_info, vlist_entry, subif.bport, vlanid, BRIDGE_FLAGS_MASTER); goto EXIT; } /* Dont allow multiple commands to add multiple entries in list */ bp_ventry = get_vlist_entry_in_bp(br_info, subif.bport, vlanid, 0); if (bp_ventry) { pr_info("%s: Vlan ID %d is already present in Bp %d of Br %s\n", __func__, vlanid, subif.bport, br_info->dev->name); goto EXIT; } bp_ventry = get_vlist_entry_in_all_bp(br_info, vlanid); br_ventry = get_vlist_entry_in_br(br_info, vlanid, 0); /* For every member port bport will be unique */ ventry = dp_add_vlan_entry(br_info, br_ventry, bp_ventry, subif.bport, vlanid, BRIDGE_FLAGS_MASTER); if (!ventry) { pr_err("%s: Failed to Add vland id %d to dev %s in br %s\n", __func__, vlanid, dev->name, br_info->dev->name); goto EXIT; } DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: Vlan ID %d Enabled Iface=%s br=%s NewFiD=%d RefCnt=%d\n", __func__, ventry->vlan_id, dev->name, br_info->dev->name, ventry->fid, ventry->ref_cnt); ret = DP_SUCCESS; EXIT: return ret; } /* Parse netlink message to get VLAN ID and Bridge Flags for Master and Self */ static int parse_netlink_msg(struct nlmsghdr *nlh, u16 *vid, u16 *br_flags) { struct nlattr *attr, *br_spec; int rem; struct bridge_vlan_info *vinfo = NULL; br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); if (!br_spec) return -EINVAL; nla_for_each_nested (attr, br_spec, rem) { if ((nla_type(attr) != IFLA_BRIDGE_FLAGS) && (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)) continue; if (nla_type(attr) == IFLA_BRIDGE_VLAN_INFO) { if (nla_len(attr) != sizeof(struct bridge_vlan_info)) return -EINVAL; vinfo = nla_data(attr); if (vinfo) *vid = vinfo->vid; } if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { if (nla_len(attr) < sizeof(u16)) return -EINVAL; *br_flags = nla_get_u16(attr); } } if (!vinfo) { pr_err("Vlan ID is not passed for VLAN Aware bridge\n"); return -EINVAL; } return DP_SUCCESS; } /* This will be called for bridge add dev eth0 vid 100 * By this time bridge interface is added to bridge */ int dp_ndo_br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags) { u16 vid; u16 br_flags = 0; struct net_device *br_dev; struct br_info *br_info; int ret = 0; if (!dev) return -EOPNOTSUPP; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: dev=%s\n", __func__, dev ? dev->name : "NULL"); if (netif_is_bridge_master(dev)) br_dev = dev; else if (netif_is_bridge_port(dev)) br_dev = netdev_master_upper_dev_get(dev); else return -EOPNOTSUPP; if (parse_netlink_msg(nlh, &vid, &br_flags)) { pr_err("%s: Error in parsing netlink message\n", __func__); ret = -EINVAL; goto EXIT; } br_info = dp_swdev_bridge_entry_lookup_rcu(br_dev); if (!br_info) { pr_err("%s: dev %s is not registered to DPM\n", __func__, br_dev->name); ret = -EINVAL; goto EXIT; } if (!br_info->br_vlan_en) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: br=%s is not VLAN Aware\n", __func__, br_dev->name); ret = -EINVAL; goto EXIT; } DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: dev=%s Flags=%s VlandID=%d\n", __func__, br_dev->name, (br_flags & BRIDGE_FLAGS_SELF) ? "Self" : "Master", vid); /* if Br Flags is not passed considering it as Master */ if (br_flags & BRIDGE_FLAGS_SELF) dp_register_self(br_info, vid, 0); else dp_register_master(br_info, dev, vid, 0); ret = 0; EXIT: return ret; } int dp_ndo_br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags) { int ret = DP_SUCCESS; u16 vid; u16 br_flags = 0; struct net_device *br_dev; struct br_info *br_info; if (!dev) return -EOPNOTSUPP; DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: dev=%s\n", __func__, dev ? dev->name : "NULL"); if (netif_is_bridge_master(dev)) br_dev = dev; else if (netif_is_bridge_port(dev)) br_dev = netdev_master_upper_dev_get(dev); else return -EOPNOTSUPP; if (parse_netlink_msg(nlh, &vid, &br_flags)) { pr_err("%s: Error in parsing netlink message\n", __func__); ret = -EINVAL; goto EXIT; } br_info = dp_swdev_bridge_entry_lookup_rcu(br_dev); if (!br_info) { pr_err("%s: dev %s is not registered to DPM\n", __func__, br_dev->name); ret = -EINVAL; goto EXIT; } if (!br_info->br_vlan_en) { DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: br=%s is not VLAN Aware\n", __func__, br_dev->name); ret = -EINVAL; goto EXIT; } DP_DEBUG(DP_DBG_FLAG_BR_VLAN, "%s: dev=%s Flags=%s VlandID=%d\n", __func__, dev->name, br_flags & BRIDGE_FLAGS_SELF ? "Self" : "Master", vid); if (br_flags & BRIDGE_FLAGS_SELF) dp_register_self(br_info, vid, BR_VLAN_DEREGISTER); else dp_register_master(br_info, dev, vid, BR_VLAN_DEREGISTER); ret = 0; EXIT: return ret; } /* FiD for VLAN can be only 6 bits, Here we need to know * maxvlanid - Max VLAN ID supported per Bridge */ int init_br_vlan(u32 maxvlanid) { if (maxvlanid > MAX_FID || maxvlanid < 1) { pr_err("VLAN Aware support max 64 FiD (VlanID + BriID)\n"); return DP_FAILURE; } g_br_vlan_cap.maxvlanid = maxvlanid; g_br_vlan_cap.maxbridgeid = MAX_FID - maxvlanid; return DP_SUCCESS; } EXPORT_SYMBOL(init_br_vlan);