blob: b1468c7eaf79f1bdda13701b0f6adbfe23a1bd5c [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* nss_bridge_mgr_ovs.c
* Handle OVS bridge notifications.
*/
#include <linux/netdevice.h>
#include <linux/notifier.h>
#include <ovsmgr.h>
#include <nss_vlan_mgr.h>
#include "nss_bridge_mgr_priv.h"
/*
* nss_bridge_mgr_ovs_handle_port_event()
* Handle OVS bridge port events
*/
static int nss_bridge_mgr_ovs_handle_port_event(struct ovsmgr_notifiers_info *ovs_info, unsigned long event)
{
struct ovsmgr_dp_port_info *port;
struct nss_bridge_pvt *b_pvt;
struct net_device *master_dev, *dev;
int err;
port = ovs_info->port;
if (!port || !port->master || !port->dev) {
nss_bridge_mgr_warn("%px: Invalid ovs_info\n", ovs_info);
return -EINVAL;
}
master_dev = port->master;
dev = port->dev;
/*
* Check if upper_dev is a known bridge.
*/
b_pvt = nss_bridge_mgr_find_instance(master_dev);
if (!b_pvt) {
nss_bridge_mgr_warn("%px: Couldn't find bridge instance for master: %s\n", port, master_dev->name);
return -ENOENT;
}
/*
* add port to the bridge.
*/
if (event == OVSMGR_DP_PORT_ADD) {
nss_bridge_mgr_trace("%px: Interface %s joining bridge %s\n", b_pvt, dev->name, master_dev->name);
err = nss_bridge_mgr_join_bridge(dev, b_pvt);
if (err) {
nss_bridge_mgr_warn("%px: Interface %s failed to join bridge %s\n", b_pvt, dev->name, master_dev->name);
return err;
}
return 0;
}
/*
* delete port from bridge.
*/
nss_bridge_mgr_trace("%px: Interface %s leaving bridge %s\n", b_pvt, dev->name, master_dev->name);
err = nss_bridge_mgr_leave_bridge(dev, b_pvt);
if (err) {
nss_bridge_mgr_warn("%px: Interface %s failed to leave bridge %s\n", b_pvt, dev->name, master_dev->name);
return err;
}
return 0;
}
/*
* nss_bridge_mgr_ovs_handle_vlan_event()
* Handle VLAN events OVS bridge port
*/
static void nss_bridge_mgr_ovs_handle_vlan_event(struct ovsmgr_notifiers_info *ovs_info, unsigned long event)
{
struct ovsmgr_dp_port_vlan_info *vlan;
struct nss_bridge_pvt *b_pvt;
struct net_device *master_dev, *dev;
vlan = ovs_info->vlan;
if (!vlan || !vlan->master || !vlan->dev) {
nss_bridge_mgr_warn("%px: Invalid ovs_info\n", ovs_info);
return;
}
master_dev = vlan->master;
dev = vlan->dev;
/*
* Check if upper_dev is a known bridge.
*/
b_pvt = nss_bridge_mgr_find_instance(master_dev);
if (!b_pvt) {
nss_bridge_mgr_warn("%px: Couldn't find bridge instance for master: %s\n", vlan, master_dev->name);
return;
}
if (event == OVSMGR_DP_VLAN_ADD) {
/*
* add VLAN in bridge.
*/
nss_bridge_mgr_trace("%px: VLAN = %d, add on port %s, bridge %s\n",
b_pvt, vlan->vh.h_vlan_TCI, dev->name, master_dev->name);
nss_vlan_mgr_add_vlan_rule(dev, b_pvt->vsi, vlan->vh.h_vlan_TCI);
return;
}
/*
* delete VLAN from bridge.
*/
nss_bridge_mgr_trace("%px: VLAN = %d, delete on port %s, bridge %s\n",
b_pvt, vlan->vh.h_vlan_TCI, dev->name, master_dev->name);
nss_vlan_mgr_del_vlan_rule(dev, b_pvt->vsi, vlan->vh.h_vlan_TCI);
}
/*
* nss_bridge_mgr_ovs_notifier_callback()
* Netdevice notifier callback to inform us of change of state of a netdevice
*/
static int nss_bridge_mgr_ovs_notifier_callback(struct notifier_block *nb, unsigned long event, void *data)
{
struct ovsmgr_notifiers_info *ovs_info = (struct ovsmgr_notifiers_info *)data;
nss_bridge_mgr_info("OVS notifier event: %lu\n", event);
switch (event) {
case OVSMGR_DP_BR_ADD:
nss_bridge_mgr_register_br(ovs_info->dev);
break;
case OVSMGR_DP_BR_DEL:
nss_bridge_mgr_unregister_br(ovs_info->dev);
break;
case OVSMGR_DP_PORT_ADD:
case OVSMGR_DP_PORT_DEL:
nss_bridge_mgr_ovs_handle_port_event(ovs_info, event);
break;
case OVSMGR_DP_VLAN_ADD:
case OVSMGR_DP_VLAN_DEL:
nss_bridge_mgr_ovs_handle_vlan_event(ovs_info, event);
break;
}
return NOTIFY_DONE;
}
/*
* struct notifier_block nss_bridge_mgr_ovs_notifier
* Registration for OVS events
*/
static struct notifier_block ovs_notifier __read_mostly = {
.notifier_call = nss_bridge_mgr_ovs_notifier_callback,
};
/*
* nss_bridge_mgr_is_ovs_port()
* Return true if dev is an OVS port.
*/
int nss_bridge_mgr_is_ovs_port(struct net_device *dev)
{
if (dev->priv_flags & IFF_OVS_DATAPATH) {
return true;
}
return false;
}
/*
* nss_bridge_mgr_ovs_exit()
* Cleanup OVS bridge handlers.
*/
void nss_bridge_mgr_ovs_exit(void)
{
ovsmgr_notifier_unregister(&ovs_notifier);
}
/*
* nss_bridge_mgr_exit_module()
* Initialize OVS bridge handlers.
*/
void nss_bridge_mgr_ovs_init(void)
{
ovsmgr_notifier_register(&ovs_notifier);
}