blob: 95747060cc0620d2a0b5c9316e3134121000eb8c [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2019-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_clmapmgr.c
* This file implements client for CLient map manager.
*/
#include <linux/version.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <nss_api_if.h>
#include <nss_dynamic_interface.h>
#include "nss_clmapmgr_private.h"
#include "nss_clmapmgr.h"
#include "nss_eogremgr.h"
#define NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT 3
#define NSS_CLMAP_MAX_HEADROOM NSS_EOGREMGR_MAX_HEADROOM
/*
* nss_clmapmgr_dev_xmit()
* Netdev ops function to send packet to NSS.
*/
static netdev_tx_t nss_clmapmgr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
nss_tx_status_t status;
int if_number;
struct nss_ctx_instance *clmap_ctx;
struct nss_clmapmgr_priv_t *priv = netdev_priv(dev);
if_number = priv->nss_if_number_us;
if (unlikely(if_number <= 0)) {
nss_clmapmgr_info("%px: clmapmgr dev is not registered with nss\n", dev);
goto fail;
}
clmap_ctx = nss_clmap_get_ctx();
if (unlikely(!clmap_ctx)) {
nss_clmapmgr_info("%px: NSS clmapmgr context not found.\n", dev);
goto fail;
}
status = nss_clmap_tx_buf(clmap_ctx, skb, (uint32_t)if_number);
if (unlikely(status != NSS_TX_SUCCESS)) {
if (likely(status == NSS_TX_FAILURE_QUEUE)) {
nss_clmapmgr_warning("%px: netdev :%px queue is full", dev, dev);
if (!netif_queue_stopped(dev)) {
netif_stop_queue(dev);
}
nss_clmapmgr_warning("%px: (CLMAP packet) Failed to xmit the packet because of tx queue full, status: %d\n", dev, status);
return NETDEV_TX_BUSY;
}
nss_clmapmgr_info("%px: NSS clmapmgr could not send packet to NSS %d\n", dev, if_number);
goto fail;
}
return NETDEV_TX_OK;
fail:
dev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
/*
* nss_clmapmgr_get_dev_stats64()
* Netdev ops function to retrieve stats.
*/
static struct rtnl_link_stats64 *nss_clmapmgr_get_dev_stats64(struct net_device *dev,
struct rtnl_link_stats64 *stats)
{
struct nss_clmapmgr_priv_t *priv;
if (!stats) {
nss_clmapmgr_warning("%px: invalid rtnl structure\n", dev);
return stats;
}
dev_hold(dev);
/*
* Netdev seems to be incrementing rx_dropped because we don't give IP header.
* So reset it as it's of no use for us.
*/
atomic_long_set(&dev->rx_dropped, 0);
priv = netdev_priv(dev);
memset(stats, 0, sizeof(struct rtnl_link_stats64));
memcpy(stats, &priv->stats, sizeof(struct rtnl_link_stats64));
dev_put(dev);
return stats;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))
/*
* nss_clmapmgr_dev_stats64()
* Netdev ops function to retrieve stats for kernel version < 4.6
*/
static struct rtnl_link_stats64 *nss_clmapmgr_dev_stats64(struct net_device *dev,
struct rtnl_link_stats64 *tot)
{
return nss_clmapmgr_get_dev_stats64(dev, tot);
}
#else
/*
* nss_clmapmgr_dev_stats64()
* Netdev ops function to retrieve stats for kernel version >= 4.6
*/
static void nss_clmapmgr_dev_stats64(struct net_device *dev,
struct rtnl_link_stats64 *tot)
{
nss_clmapmgr_get_dev_stats64(dev, tot);
}
#endif
/*
* nss_clmapmgr_dev_init()
* Netdev ops function to intialize netdevice.
*/
static int nss_clmapmgr_dev_init(struct net_device *dev)
{
dev->mtu = ETH_DATA_LEN;
dev->needed_headroom = NSS_CLMAP_MAX_HEADROOM;
return 0;
}
/*
* nss_clmapmgr_dev_open()
* Netdev ops function to open netdevice.
*/
static int nss_clmapmgr_dev_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
/*
* nss_clmapmgr_dev_close()
* Netdevice ops function to close netdevice.
*/
static int nss_clmapmgr_dev_close(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
/*
* clmap netdevice ops
*/
static const struct net_device_ops nss_clmapmgr_ops = {
.ndo_init = nss_clmapmgr_dev_init,
.ndo_open = nss_clmapmgr_dev_open,
.ndo_stop = nss_clmapmgr_dev_close,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_start_xmit = nss_clmapmgr_dev_xmit,
.ndo_get_stats64 = nss_clmapmgr_dev_stats64,
};
/*
* nss_clmapmgr_setup()
*/
static void nss_clmapmgr_setup(struct net_device *dev)
{
char name[IFNAMSIZ] = {0};
strlcpy(name, "nssclmap%d", IFNAMSIZ);
memcpy(dev->name, name, IFNAMSIZ);
dev->netdev_ops = &nss_clmapmgr_ops;
eth_hw_addr_random(dev);
}
/*
* nss_clmapmgr_ds_exception()
* Client map manager ds exception handler to receive packet from NSS.
*/
static void nss_clmapmgr_ds_exception(struct net_device *dev, struct sk_buff *skb,
__attribute__((unused)) struct napi_struct *napi)
{
/*
* Note: preheader needs to be processed by the user
* before processing the ethernet packet
*/
skb->protocol = eth_type_trans(skb, dev);
netif_receive_skb(skb);
}
/*
* nss_clmapmgr_us_exception()
* Client map manager us exception handler to receive packet from NSS.
*/
static void nss_clmapmgr_us_exception(struct net_device *dev, struct sk_buff *skb,
__attribute__((unused)) struct napi_struct *napi)
{
/*
* This is an error packet and needs to be dropped.
*/
nss_clmapmgr_warning("%px: upstream packet got exceptioned, dropping the packet..", dev);
dev_kfree_skb_any(skb);
}
/*
* nss_clmapmgr_event_receive()
* Event Callback to receive events from NSS
*/
static void nss_clmapmgr_event_receive(void *if_ctx, struct nss_cmn_msg *cmsg)
{
struct net_device *dev = (struct net_device *)if_ctx;
struct nss_clmapmgr_priv_t *priv;
struct nss_clmap_msg *clmsg = (struct nss_clmap_msg *)cmsg;
struct nss_clmap_stats_msg *stats = &clmsg->msg.stats;
struct rtnl_link_stats64 *netdev_stats;
enum nss_dynamic_interface_type interface_type;
uint64_t dropped = 0;
dev_hold(dev);
priv = netdev_priv(dev);
netdev_stats = &priv->stats;
switch (clmsg->cm.type) {
case NSS_CLMAP_MSG_TYPE_SYNC_STATS:
interface_type = nss_dynamic_interface_get_type(nss_clmap_get_ctx(), clmsg->cm.interface);
if (interface_type == NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US) {
netdev_stats->tx_packets += stats->node_stats.tx_packets;
netdev_stats->tx_bytes += stats->node_stats.tx_bytes;
dropped += stats->dropped_macdb_lookup_failed;
dropped += stats->dropped_invalid_packet_size;
dropped += stats->dropped_low_hroom;
} else if (interface_type == NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS) {
netdev_stats->rx_packets += stats->node_stats.rx_packets;
netdev_stats->rx_bytes += stats->node_stats.rx_bytes;
dropped += stats->dropped_pbuf_alloc_failed;
dropped += stats->dropped_linear_failed;
dropped += stats->shared_packet_count;
dropped += stats->ethernet_frame_error;
}
dropped += stats->dropped_next_node_queue_full;
netdev_stats->tx_dropped += dropped;
if (interface_type == NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS) {
netdev_stats->rx_dropped += nss_cmn_rx_dropped_sum(&stats->node_stats);
}
break;
default:
nss_clmapmgr_info("%px: Unknown Event from NSS\n", dev);
break;
}
dev_put(dev);
}
/*
* nss_clmapmgr_us_get_if_num
* return us NSS interface number
*/
int nss_clmapmgr_us_get_if_num(struct net_device *dev)
{
struct nss_clmapmgr_priv_t *priv;
if (!dev) {
nss_clmapmgr_info("Netdev is NULL");
return -1;
}
priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
return priv->nss_if_number_us;
}
EXPORT_SYMBOL(nss_clmapmgr_us_get_if_num);
/*
* nss_clmapmgr_ds_get_if_num
* return ds NSS interface number
*/
int nss_clmapmgr_ds_get_if_num(struct net_device *dev)
{
struct nss_clmapmgr_priv_t *priv;
if (!dev) {
nss_clmapmgr_info("Netdev is NULL");
return -1;
}
priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
return priv->nss_if_number_ds;
}
EXPORT_SYMBOL(nss_clmapmgr_ds_get_if_num);
/*
* nss_clmapmgr_mac_add()
* API to send notification to NSS to add the MAC entry.
*/
nss_clmapmgr_status_t nss_clmapmgr_mac_add(struct net_device *dev, struct nss_clmapmgr_msg *clmapmsg)
{
struct nss_clmap_msg req;
struct nss_clmap_mac_msg *mmsg;
int us_if, next_ifnum;
struct nss_ctx_instance *nss_ctx = NULL;
nss_tx_status_t status;
if (!dev) {
nss_clmapmgr_info("Netdev is NULL !!\n");
return NSS_CLMAPMGR_ERR_BAD_PARAM;
}
if (!clmapmsg) {
nss_clmapmgr_info("%px: nss_clmapmgr_msg is NULL !!\n", dev);
return NSS_CLMAPMGR_ERR_BAD_PARAM;
}
/*
* Get Interface number, based on tunnel type
*/
switch (clmapmsg->tunnel_type) {
case NSS_CLMAPMGR_TUNNEL_EOGRE:
/*
* For EoGRE tunnel, the next node for the packet from clmap node in NSS
* would be GRE inner node. Get the GRE inner interface.
*/
next_ifnum = nss_eogremgr_get_if_num_inner(clmapmsg->tunnel_id);
if (next_ifnum < 0) {
nss_clmapmgr_info("%px: No NSS interface registered for the tunnel id: %d\n", dev, clmapmsg->tunnel_id);
return NSS_CLMAPMGR_ERR_TUNNEL_NOT_FOUND;
}
break;
default:
nss_clmapmgr_info("%px: Invalid tunnel type: %d\n", dev, clmapmsg->tunnel_type);
return NSS_CLMAPMGR_ERR_BAD_PARAM;
}
nss_ctx = nss_clmap_get_ctx();
/*
* Check if upstream clmap interface is registered with NSS.
*/
us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (us_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with nss\n", dev);
dev_put(dev);
return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
}
memset(&req, 0, sizeof(struct nss_clmap_msg));
mmsg = &req.msg.mac_add;
/*
* Set mac_add message.
*/
memcpy(mmsg->mac_addr, clmapmsg->mac_addr, ETH_ALEN);
mmsg->flags = clmapmsg->flags;
mmsg->vlan_id = clmapmsg->vlan_id;
mmsg->needed_headroom = clmapmsg->needed_headroom;
mmsg->nexthop_ifnum = next_ifnum;
nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_MAC_ADD, sizeof(struct nss_clmap_mac_msg), NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: nss clmap mac add command error:%d if_num: %d\n", dev, status, us_if);
dev_put(dev);
return NSS_CLMAPMGR_ERR_MAC_ADD_FAILED;
}
return NSS_CLMAPMGR_SUCCESS;
}
EXPORT_SYMBOL(nss_clmapmgr_mac_add);
/*
* nss_clmapmgr_mac_remove()
* API to send notification to NSS to delete the MAC entry.
*/
nss_clmapmgr_status_t nss_clmapmgr_mac_remove(struct net_device *dev, uint8_t *mac_addr)
{
struct nss_clmap_msg req;
struct nss_clmap_mac_msg *mmsg;
int us_if;
struct nss_ctx_instance *nss_ctx = NULL;
nss_tx_status_t status;
if (!dev) {
nss_clmapmgr_info("Netdev is NULL !!\n");
return NSS_CLMAPMGR_ERR_BAD_PARAM;
}
if (!mac_addr) {
nss_clmapmgr_info("%px: mac address is NULL !!\n", dev);
return NSS_CLMAPMGR_ERR_BAD_PARAM;
}
nss_ctx = nss_clmap_get_ctx();
/*
* Check if upstream clmap interface is registered with NSS
*/
us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (us_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with nss\n", dev);
dev_put(dev);
return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
}
memset(&req, 0, sizeof(struct nss_clmap_msg));
mmsg = &req.msg.mac_add;
/*
* Set mac_del message. Only MAC address is required, the other
* fields as set to 0.
*
*/
memcpy(mmsg->mac_addr, mac_addr, ETH_ALEN);
nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_MAC_DEL, sizeof(struct nss_clmap_mac_msg), NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: NSS clmap mac del command error:%d if_num: %d\n", dev, status, us_if);
dev_put(dev);
return NSS_CLMAPMGR_ERR_MAC_DEL_FAILED;
}
return NSS_CLMAPMGR_SUCCESS;
}
EXPORT_SYMBOL(nss_clmapmgr_mac_remove);
/*
* nss_clmapmgr_mac_flush()
* API to send notification to NSS to flush MAC entry.
*/
nss_clmapmgr_status_t nss_clmapmgr_mac_flush(struct net_device *dev, uint32_t tunnel_id, nss_clmapmgr_tunnel_type_t tunnel_type)
{
struct nss_clmap_msg req;
struct nss_clmap_flush_mac_msg *mmsg = &req.msg.mac_flush;
int us_if, next_ifnum;
struct nss_ctx_instance *nss_ctx = NULL;
nss_tx_status_t status;
if (!dev) {
nss_clmapmgr_info("Netdev is NULL !!\n");
return NSS_CLMAPMGR_ERR_BAD_PARAM;
}
switch (tunnel_type) {
case NSS_CLMAPMGR_TUNNEL_EOGRE:
/*
* Get GRE inner interface number
*/
next_ifnum = nss_eogremgr_get_if_num_inner(tunnel_id);
if (next_ifnum < 0) {
nss_clmapmgr_info("%px: No NSS interface registered for the tunnel id: %d\n", dev, tunnel_id);
return NSS_CLMAPMGR_ERR_TUNNEL_NOT_FOUND;
}
break;
default:
nss_clmapmgr_info("%px: Invalid tunnel type: %d\n", dev, tunnel_type);
return NSS_CLMAPMGR_ERR_BAD_PARAM;
}
nss_ctx = nss_clmap_get_ctx();
/*
* Check if upstream clmap interface is registered with NSS
*/
us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (us_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with nss\n", dev);
dev_put(dev);
return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
}
/*
* Set mac_flush message
*/
mmsg->nexthop_ifnum = next_ifnum;
nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_MAC_FLUSH, sizeof(struct nss_clmap_mac_msg), NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: NSS clmap mac flush command error:%d if_num: %d\n", dev, status, us_if);
dev_put(dev);
return NSS_CLMAPMGR_ERR_MAC_FLUSH_FAILED;
}
return NSS_CLMAPMGR_SUCCESS;
}
EXPORT_SYMBOL(nss_clmapmgr_mac_flush);
/*
* nss_clmapmgr_netdev_enable()
* Call back to enable NSS for clmap device.
*/
int nss_clmapmgr_netdev_enable(struct net_device *dev)
{
struct nss_clmap_msg req;
int us_if, ds_if;
struct nss_ctx_instance *nss_ctx = NULL;
struct nss_clmapmgr_priv_t *priv;
nss_tx_status_t status;
if (!dev) {
nss_clmapmgr_info("Netdev is NULL !!\n");
return NOTIFY_DONE;
}
nss_ctx = nss_clmap_get_ctx();
/*
* Check if upstream clmap interface is registered with NSS
*/
us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (us_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with nss\n", dev);
goto release_ref;
}
/*
* Check if downstream clmap interface is registered with NSS
*/
ds_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
if (ds_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with nss\n", dev);
goto release_ref;
}
/*
* Send enable session command for upstream interface
*/
nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_INTERFACE_ENABLE, 0, NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: NSS clmap enable command error:%d if_num: %d\n", dev, status, us_if);
goto release_ref;
}
/*
* Send enable session command for downstream interface
*/
nss_clmap_msg_init(&req, ds_if, NSS_CLMAP_MSG_TYPE_INTERFACE_ENABLE, 0, NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: NSS clmap enable command error:%d if_num: %d\n", dev, status, ds_if);
goto disable_us;
}
/*
* Open the netdev to accept packets
*/
priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
priv->clmap_enabled = true;
nss_clmapmgr_dev_open(dev);
return NOTIFY_OK;
disable_us:
nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_INTERFACE_DISABLE, 0, NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: NSS clmap enable command error:%d if_num: %d\n", dev, status, us_if);
}
release_ref:
return NOTIFY_DONE;
}
EXPORT_SYMBOL(nss_clmapmgr_netdev_enable);
/*
* nss_clmapmgr_netdev_disable()
* Call back to disable clmap interface in NSS.
*/
int nss_clmapmgr_netdev_disable(struct net_device *dev)
{
struct nss_clmap_msg req;
int us_if, ds_if;
struct nss_ctx_instance *nss_ctx = NULL;
struct nss_clmapmgr_priv_t *priv;
nss_tx_status_t status;
if (!dev) {
nss_clmapmgr_info("Netdev is NULL !!\n");
return NOTIFY_DONE;
}
nss_ctx = nss_clmap_get_ctx();
/*
* Check if upstream clmap interface is registered with NSS
*/
us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (us_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with NSS\n", dev);
goto release_ref;
}
/*
* Check if downstream clmap interface is registered with NSS
*/
ds_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
if (ds_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with NSS\n", dev);
goto release_ref;
}
/*
* Send disable session command for upstream interface
*/
nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_INTERFACE_DISABLE, 0, NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: NSS clmap disable command error:%d if_num: %d\n", dev, status, us_if);
goto release_ref;
}
/*
* Send disable session command for downstream interface
*/
nss_clmap_msg_init(&req, ds_if, NSS_CLMAP_MSG_TYPE_INTERFACE_DISABLE, 0, NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: NSS clmap disable command error:%d if_num: %d\n", dev, status, ds_if);
goto enable_us;
}
/*
* Close the netdev
*/
priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
priv->clmap_enabled = false;
nss_clmapmgr_dev_close(dev);
return NOTIFY_OK;
enable_us:
nss_clmap_msg_init(&req, us_if, NSS_CLMAP_MSG_TYPE_INTERFACE_ENABLE, 0, NULL, NULL);
status = nss_clmap_tx_msg_sync(nss_ctx, &req);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_warning("%px: NSS clmap disable command error:%d if_num: %d\n", dev, status, us_if);
}
release_ref:
return NOTIFY_DONE;
}
EXPORT_SYMBOL(nss_clmapmgr_netdev_disable);
/*
* nss_clmapmgr_dev_event()
* Netdevice notifier call back function.
*/
static int nss_clmapmgr_dev_event(struct notifier_block *nb,
unsigned long event, void *dev)
{
struct net_device *netdev;
netdev = netdev_notifier_info_to_dev(dev);
switch (event) {
case NETDEV_UP:
return nss_clmapmgr_netdev_enable(netdev);
case NETDEV_DOWN:
return nss_clmapmgr_netdev_disable(netdev);
default:
break;
}
return NOTIFY_DONE;
}
/*
* nss_clmapmgr_destroy_us_interface()
* Destroy upstream clmap interface.
*/
static nss_clmapmgr_status_t nss_clmapmgr_destroy_us_interface(struct net_device *dev, int interface_num)
{
nss_tx_status_t status;
int retry = 0;
if (!nss_clmap_unregister(interface_num)) {
nss_clmapmgr_warning("%px: clmap NSS upstream interface unregister failed\n.", dev);
return NSS_CLMAPMGR_ERR_NSSIF_UNREGISTER_FAILED;
}
dealloc_us:
status = nss_dynamic_interface_dealloc_node(interface_num, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_info("%px: clmap dealloc node failure for interface_num = %d\n", dev, interface_num);
if (++retry <= NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT) {
goto dealloc_us;
}
nss_clmapmgr_error("%px: fatal Error, failed to dealloc upstream clmap NSS interface.\n", dev);
return NSS_CLMAPMGR_ERR_NSSIF_DEALLOC_FAILED;
}
return NSS_CLMAPMGR_SUCCESS;
}
/*
* nss_clmapmgr_destroy_ds_interface()
* Destroy downstream clmap interface.
*/
static nss_clmapmgr_status_t nss_clmapmgr_destroy_ds_interface(struct net_device *dev, int interface_num)
{
nss_tx_status_t status;
int retry = 0;
if (!nss_clmap_unregister(interface_num)) {
nss_clmapmgr_warning("%px: clmap NSS downstream interface unregister failed\n.", dev);
return NSS_CLMAPMGR_ERR_NSSIF_UNREGISTER_FAILED;
}
dealloc_ds:
status = nss_dynamic_interface_dealloc_node(interface_num, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
if (status != NSS_TX_SUCCESS) {
nss_clmapmgr_info("%px: clmap dealloc node failure for ds_if = %d\n", dev, interface_num);
if (++retry <= NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT) {
goto dealloc_ds;
}
nss_clmapmgr_error("%px: fatal Error, failed to dealloc downstream clmap NSS interface.\n", dev);
return NSS_CLMAPMGR_ERR_NSSIF_DEALLOC_FAILED;
}
return NSS_CLMAPMGR_SUCCESS;
}
/*
* nss_clmapmgr_decongestion_callback()
* Wakeup netif queue if we were stopped by start_xmit
*/
static void nss_clmapmgr_decongestion_callback(void *arg) {
struct net_device *dev = arg;
struct nss_clmapmgr_priv_t *priv;
priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
if (unlikely(!priv->clmap_enabled)) {
return;
}
if (netif_queue_stopped(dev)) {
netif_wake_queue(dev);
}
}
/*
* nss_clmapmgr_netdev_destroy()
* API for destroying a netdevice.
* Note: User needs to flush all MAC entries in the clmap before destroying the clmap netdevice
*/
nss_clmapmgr_status_t nss_clmapmgr_netdev_destroy(struct net_device *dev)
{
int us_if, ds_if;
nss_clmapmgr_status_t ret;
netif_tx_disable(dev);
/*
* Deregister decongestion callback
*/
if (nss_cmn_unregister_queue_decongestion(nss_clmap_get_ctx(), nss_clmapmgr_decongestion_callback) != NSS_CB_UNREGISTER_SUCCESS) {
nss_clmapmgr_info("%px: failed to unregister decongestion callback\n", dev);
}
/*
* Check if upstream clmap interface is registered with NSS
*/
us_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (us_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with NSS\n", dev);
return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
}
/*
* Check if downstream clmap interface is registered with NSS
*/
ds_if = nss_cmn_get_interface_number_by_dev_and_type(dev, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
if (ds_if < 0) {
nss_clmapmgr_info("%px: Net device is not registered with NSS\n", dev);
return NSS_CLMAPMGR_ERR_NETDEV_UNKNOWN;
}
ret = nss_clmapmgr_destroy_us_interface(dev, us_if);
if (ret != NSS_CLMAPMGR_SUCCESS) {
nss_clmapmgr_warning("%px: failed to destroy clmap upstream interface: %d\n", dev, us_if);
return ret;
}
ret = nss_clmapmgr_destroy_ds_interface(dev, ds_if);
if (ret != NSS_CLMAPMGR_SUCCESS) {
nss_clmapmgr_warning("%px: failed to destroy clmap downstream interface: %d\n", dev, ds_if);
return ret;
}
nss_clmapmgr_info("%px: deleted clmap instance, us_if = %d ds_if = %d\n",
dev, us_if, ds_if);
unregister_netdev(dev);
free_netdev(dev);
return NSS_CLMAPMGR_SUCCESS;
}
EXPORT_SYMBOL(nss_clmapmgr_netdev_destroy);
/*
* nss_clmapmgr_netdev_create()
* User API to create clmap interface
*/
struct net_device *nss_clmapmgr_netdev_create(void)
{
struct nss_ctx_instance *nss_ctx;
struct net_device *dev = NULL;
struct nss_clmapmgr_priv_t *priv;
nss_tx_status_t status;
uint32_t features = 0;
int32_t us_if, ds_if;
int ret = -1, retry = 0;
dev = alloc_etherdev(sizeof(struct nss_clmapmgr_priv_t));
if (!dev) {
nss_clmapmgr_warning("Allocation of netdev failed\n");
return NULL;
}
nss_clmapmgr_setup(dev);
/*
* Register net_device
*/
ret = register_netdev(dev);
if (ret) {
nss_clmapmgr_warning("%px: Netdevice registration failed\n", dev);
free_netdev(dev);
return NULL;
}
/*
* Create NSS clmap downstream dynamic interface
*/
ds_if = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
if (ds_if < 0) {
nss_clmapmgr_warning("%px: NSS dynamic interface alloc failed for clmap downstream\n", dev);
goto deregister_netdev;
}
/*
* Create NSS clmap upstream dynamic interface
*/
us_if = nss_dynamic_interface_alloc_node(NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (us_if < 0) {
nss_clmapmgr_warning("%px: NSS dynamic interface alloc failed for clmap upstream\n", dev);
goto dealloc_ds_node;
}
priv = (struct nss_clmapmgr_priv_t *)netdev_priv(dev);
priv->clmap_enabled = false;
priv->nss_if_number_us = us_if;
priv->nss_if_number_ds = ds_if;
/*
* Register downstream clmap interface with NSS
*/
nss_ctx = nss_clmap_register(ds_if,
NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS,
nss_clmapmgr_ds_exception,
nss_clmapmgr_event_receive,
dev,
features);
if (!nss_ctx) {
nss_clmapmgr_info("%px: nss_clmap_register failed for downstream interface\n", dev);
goto dealloc_us_node;
}
/*
* Register upstream clmap interface with NSS
*/
nss_ctx = nss_clmap_register(us_if,
NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US,
nss_clmapmgr_us_exception,
nss_clmapmgr_event_receive,
dev,
features);
if (!nss_ctx) {
nss_clmapmgr_info("%px: nss_clmap_register failed for upstream interface\n", dev);
goto unregister_ds;
}
/*
* Register decongestion callback
*/
if (nss_cmn_register_queue_decongestion(nss_clmap_get_ctx(), nss_clmapmgr_decongestion_callback, dev) != NSS_CB_REGISTER_SUCCESS) {
nss_clmapmgr_warning("%px: failed to register decongestion callback\n", dev);
goto unregister_us;
}
/*
* Success
*/
nss_clmapmgr_info("%px: nss_clmap_register() successful. nss_ctx = %px\n", dev, nss_ctx);
return dev;
unregister_us:
nss_clmap_unregister(us_if);
unregister_ds:
nss_clmap_unregister(ds_if);
dealloc_us_node:
status = nss_dynamic_interface_dealloc_node(us_if, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_US);
if (status != NSS_TX_SUCCESS) {
if (++retry <= NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT) {
goto dealloc_us_node;
}
nss_clmapmgr_error("%px: fatal Error, Unable to dealloc the node[%d] in the NSS FW!\n", dev, us_if);
}
retry = 0;
dealloc_ds_node:
status = nss_dynamic_interface_dealloc_node(ds_if, NSS_DYNAMIC_INTERFACE_TYPE_CLMAP_DS);
if (status != NSS_TX_SUCCESS) {
if (++retry <= NSS_CLMAPMGR_CMD_MAX_RETRY_COUNT) {
goto dealloc_ds_node;
}
nss_clmapmgr_error("%px: fatal Error, Unable to dealloc the node[%d] in the NSS FW!\n", dev, ds_if);
}
deregister_netdev:
unregister_netdev(dev);
free_netdev(dev);
return NULL;
}
EXPORT_SYMBOL(nss_clmapmgr_netdev_create);
/*
* Linux Net device Notifier
*/
static struct notifier_block nss_clmapmgr_notifier = {
.notifier_call = nss_clmapmgr_dev_event,
};
/*
* nss_clmapmgr_dev_init_module()
* Client map module init function
*/
static int __init nss_clmapmgr_dev_init_module(void)
{
#ifdef CONFIG_OF
/*
* If the node is not compatible, don't do anything.
*/
if (!of_find_node_by_name(NULL, "nss-common")) {
return 0;
}
#endif
register_netdevice_notifier(&nss_clmapmgr_notifier);
return 0;
}
/*
* nss_clmapmgr_exit_module
* Client map module exit function
*/
static void __exit nss_clmapmgr_exit_module(void)
{
#ifdef CONFIG_OF
/*
* If the node is not compatible, don't do anything.
*/
if (!of_find_node_by_name(NULL, "nss-common")) {
return;
}
#endif
/*
* Unregister clmap interfaces created
*/
unregister_netdevice_notifier(&nss_clmapmgr_notifier);
}
module_init(nss_clmapmgr_dev_init_module);
module_exit(nss_clmapmgr_exit_module);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("NSS client map manager");