// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <net/netevent.h>
#include <net/switchdev.h>
#include "prestera.h"
#include "prestera_hw.h"
#include "prestera_switchdev.h"
#define PRESTERA_VID_ALL (0xffff)
#define PRESTERA_DEFAULT_AGEING_TIME_MS 300000
#define PRESTERA_MAX_AGEING_TIME_MS 1000000000
#define PRESTERA_MIN_AGEING_TIME_MS 32000
struct prestera_fdb_event_work {
struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info;
struct net_device *dev;
unsigned long event;
};
struct prestera_switchdev {
struct prestera_switch *sw;
struct list_head bridge_list;
bool bridge_8021q_exists;
struct notifier_block swdev_nb_blk;
struct notifier_block swdev_nb;
};
struct prestera_bridge {
struct list_head head;
struct net_device *dev;
struct prestera_switchdev *swdev;
struct list_head port_list;
bool vlan_enabled;
u16 bridge_id;
};
struct prestera_bridge_port {
struct list_head head;
struct net_device *dev;
struct prestera_bridge *bridge;
struct list_head vlan_list;
refcount_t ref_count;
unsigned long flags;
u8 stp_state;
};
struct prestera_bridge_vlan {
struct list_head head;
struct list_head port_vlan_list;
u16 vid;
};
struct prestera_port_vlan {
struct list_head br_vlan_head;
struct list_head port_head;
struct prestera_port *port;
struct prestera_bridge_port *br_port;
u16 vid;
};
static struct workqueue_struct *swdev_wq;
static void prestera_bridge_port_put(struct prestera_bridge_port *br_port);
static int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid,
u8 state);
static struct prestera_bridge_vlan *
prestera_bridge_vlan_create(struct prestera_bridge_port *br_port, u16 vid)
{
struct prestera_bridge_vlan *br_vlan;
br_vlan = kzalloc(sizeof(*br_vlan), GFP_KERNEL);
if (!br_vlan)
return NULL;
INIT_LIST_HEAD(&br_vlan->port_vlan_list);
br_vlan->vid = vid;
list_add(&br_vlan->head, &br_port->vlan_list);
return br_vlan;
}
static void prestera_bridge_vlan_destroy(struct prestera_bridge_vlan *br_vlan)
{
list_del(&br_vlan->head);
WARN_ON(!list_empty(&br_vlan->port_vlan_list));
kfree(br_vlan);
}
static struct prestera_bridge_vlan *
prestera_bridge_vlan_by_vid(struct prestera_bridge_port *br_port, u16 vid)
{
struct prestera_bridge_vlan *br_vlan;
list_for_each_entry(br_vlan, &br_port->vlan_list, head) {
if (br_vlan->vid == vid)
return br_vlan;
}
return NULL;
}
static int prestera_bridge_vlan_port_count(struct prestera_bridge *bridge,
u16 vid)
{
struct prestera_bridge_port *br_port;
struct