blob: a5411cec9ade3d6c6c0b620f0408654208f64e6b [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2020-2021, 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.
**************************************************************************
*/
/*
* Header file for qca-ssdk APIs
*/
#include <ref/ref_vsi.h>
#include "nss_ppe_vp.h"
#include "nss_ppe_vp_stats.h"
#define NSS_PPE_VP_TX_TIMEOUT 1000 /* 1 Second */
static struct nss_vp_mapping *vp_map[NSS_MAX_DYNAMIC_INTERFACES] = {NULL};
unsigned char nss_ppe_vp_cmd[NSS_PPE_VP_MAX_CMD_STR] __read_mostly;
/*
* Private data structure
*/
static struct nss_ppe_vp_pvt {
struct semaphore sem;
struct completion complete;
int response;
void *cb;
void *app_data;
nss_ppe_port_t ppe_port_num;
} ppe_vp_pvt;
DEFINE_SPINLOCK(nss_ppe_vp_stats_lock);
DEFINE_SPINLOCK(nss_ppe_vp_map_lock);
struct nss_ppe_vp_stats_debug nss_ppe_vp_debug_stats;
static struct dentry *nss_ppe_vp_dentry;
/*
* nss_ppe_vp_get_map_index()
* Get the index of the NSS-VP number mapping array.
*/
static inline int32_t nss_ppe_vp_get_map_index(nss_if_num_t if_num)
{
return (if_num - NSS_DYNAMIC_IF_START);
}
/*
* nss_ppe_vp_verify_ifnum()
* Verify PPE VP interface number.
*/
static inline bool nss_ppe_vp_verify_ifnum(int if_num)
{
return (if_num == NSS_PPE_VP_INTERFACE);
}
/*
* nss_ppe_vp_map_dealloc()
* Deallocate memory for the NSS interface number and PPE VP number mapping.
*/
static inline void nss_ppe_vp_map_dealloc(struct nss_vp_mapping *map)
{
vfree(map);
}
/*
* nss_ppe_vp_map_alloc()
* Allocate memory for the NSS interface number and PPE VP number mapping.
*/
static inline struct nss_vp_mapping *nss_ppe_vp_map_alloc(void)
{
struct nss_vp_mapping *nss_vp_info = vzalloc(sizeof(struct nss_vp_mapping));
if (!nss_vp_info) {
nss_warning("No memory for allocating NSS-VP mapping instance");
}
return nss_vp_info;
}
/*
* nss_ppe_vp_proc_help()
* Print usage information for ppe_vp configure sysctl.
*/
static void nss_ppe_vp_proc_help(void)
{
nss_info_always("== for dynamic interface types read following file ==");
nss_info_always("/sys/kernel/debug/qca-nss-drv/stats/dynamic_if/type_names");
nss_info_always("NSS PPE VP create: echo <interface name> <dynamic interface type> > /proc/sys/nss/ppe_vp/create");
nss_info_always("NSS PPE VP destroy: echo <interface name> <dynamic interface type> > /proc/sys/nss/ppe_vp/destroy");
}
/*
* nss_ppe_vp_del_map()
* Delete mapping between NSS interface number and VP number.
*/
static bool nss_ppe_vp_del_map(struct nss_ctx_instance *nss_ctx, nss_if_num_t if_num)
{
int32_t idx;
nss_ppe_port_t ppe_port_num;
struct nss_vp_mapping *nss_vp_info;
uint16_t vp_index;
nss_assert((if_num >= NSS_DYNAMIC_IF_START) && (if_num < (NSS_DYNAMIC_IF_START + NSS_MAX_DYNAMIC_INTERFACES)));
idx = nss_ppe_vp_get_map_index(if_num);
if ((idx < 0) || (idx >= NSS_MAX_DYNAMIC_INTERFACES)) {
nss_warning("%px: Invalid index. Cannot delete the PPE VP mapping. idx:%u", nss_ctx, idx);
return false;
}
spin_lock_bh(&nss_ppe_vp_map_lock);
nss_vp_info = vp_map[idx];
if (!nss_vp_info) {
spin_unlock_bh(&nss_ppe_vp_map_lock);
nss_warning("%px: Could not find the vp num in the mapping. NSS if num:%u", nss_ctx, if_num);
return false;
}
ppe_port_num = nss_vp_info->ppe_port_num;
nss_ppe_vp_map_dealloc(nss_vp_info);
vp_map[idx] = NULL;
spin_unlock_bh(&nss_ppe_vp_map_lock);
/*
* Clear the PPE VP stats once PPE VP is deleted
*/
vp_index = ppe_port_num - NSS_PPE_VP_START;
spin_lock_bh(&nss_ppe_vp_stats_lock);
memset(&nss_ppe_vp_debug_stats.vp_stats[vp_index], 0, sizeof(struct nss_ppe_vp_statistics_debug));
spin_unlock_bh(&nss_ppe_vp_stats_lock);
nss_info("%px: Deleted NSS interface number and PPE VP number mapping successfully: NSS if num:%u at index:%u", nss_ctx, if_num, idx);
return true;
}
/*
* nss_ppe_vp_add_map()
* Add mapping between NSS interface number and VP number.
*/
static bool nss_ppe_vp_add_map(struct nss_ctx_instance *nss_ctx ,nss_if_num_t if_num, struct nss_vp_mapping *nss_vp_info)
{
uint32_t idx;
nss_ppe_port_t ppe_port_num;
nss_assert((if_num >= NSS_DYNAMIC_IF_START) && (if_num < (NSS_DYNAMIC_IF_START + NSS_MAX_DYNAMIC_INTERFACES)));
if (!nss_vp_info) {
nss_warning("%px: Received invalid argument.", nss_ctx);
return false;
}
idx = nss_ppe_vp_get_map_index(if_num);
if ((idx < 0) || (idx >= NSS_MAX_DYNAMIC_INTERFACES)) {
nss_warning("%px: Invalid index. Cannot add the PPE VP mapping. idx:%u", nss_ctx, idx);
return false;
}
spin_lock_bh(&nss_ppe_vp_map_lock);
if (vp_map[idx]) {
spin_unlock_bh(&nss_ppe_vp_map_lock);
nss_warning("%px: Mapping exists already. NSS if num:%d index:%u, VP num:%u", nss_ctx, if_num, idx, vp_map[idx]->ppe_port_num);
return false;
}
vp_map[idx] = nss_vp_info;
ppe_port_num = vp_map[idx]->ppe_port_num;
spin_unlock_bh(&nss_ppe_vp_map_lock);
nss_info("%px: Mapping added successfully. NSS if num:%d index:%u, VP num:%u", nss_ctx, if_num, idx, ppe_port_num);
return true;
}
/*
* nss_ppe_vp_callback()
* Callback to handle the completion of NSS->HLOS messages.
*/
static void nss_ppe_vp_callback(void *app_data, struct nss_ppe_vp_msg *npvm)
{
if (npvm->cm.response != NSS_CMN_RESPONSE_ACK) {
nss_warning("ppe_vp error response %d", npvm->cm.response);
ppe_vp_pvt.response = NSS_TX_FAILURE;
complete(&ppe_vp_pvt.complete);
return;
}
if (npvm->cm.type == NSS_IF_PPE_PORT_CREATE) {
ppe_vp_pvt.ppe_port_num = npvm->msg.if_msg.ppe_port_create.ppe_port_num;
nss_trace("PPE VP callback success VP num: %u", npvm->msg.if_msg.ppe_port_create.ppe_port_num);
}
ppe_vp_pvt.response = NSS_TX_SUCCESS;
complete(&ppe_vp_pvt.complete);
}
/*
* nss_ppe_vp_parse_vp_cmd()
* Parse PPE VP create and destroy message and return the NSS interface number.
* Command usage:
* echo <interface name> <dynamic interface type> /proc/sys/nss/ppe_vp/create>
* echo ath0 6 > /proc/sys/nss/ppe_vp/create
* Since ath0 has only one type i.e. ath0 is NSS_DYNAMIC_INTERFACE_TYPE_VAP, the above command can be rewritten as
* echo ath0 > /proc/sys/nss/ppe_vp/create => Here 6 can be ignored.
*/
static nss_if_num_t nss_ppe_vp_parse_vp_cmd(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
{
int32_t if_num;
struct net_device *dev;
uint32_t dynamic_if_type = (uint32_t)NSS_DYNAMIC_INTERFACE_TYPE_NONE;
struct nss_ctx_instance *nss_ctx = nss_ppe_vp_get_context();
char *pos;
char cmd_buf[NSS_PPE_VP_MAX_CMD_STR] = {0}, dev_name[NSS_PPE_VP_MAX_CMD_STR] = {0};
size_t count = *lenp;
int ret = proc_dostring(ctl, write, buffer, lenp, ppos);
if (!write) {
nss_ppe_vp_proc_help();
return ret;
}
if (!nss_ctx) {
nss_warning("%px: NSS Context not found.", nss_ctx);
return -ENODEV;
}
NSS_VERIFY_CTX_MAGIC(nss_ctx);
if (count >= NSS_PPE_VP_MAX_CMD_STR) {
nss_ppe_vp_proc_help();
nss_warning("%px: Input string too big", nss_ctx);
return -E2BIG;
}
if (copy_from_user(cmd_buf, buffer, count)) {
nss_warning("%px: Cannot copy user's entry to kernel memory", nss_ctx);
return -EFAULT;
}
if ((pos = strrchr(cmd_buf, '\n')) != NULL) {
*pos = '\0';
}
if (sscanf(cmd_buf, "%s %u", dev_name, &dynamic_if_type) < 0) {
nss_warning("%px: PPE VP command parse failed", nss_ctx);
return -EFAULT;
}
dev = dev_get_by_name(&init_net, dev_name);
if (!dev) {
nss_warning("%px: Cannot find the net device", nss_ctx);
return -ENODEV;
}
nss_info("%px: Dynamic interface type: %u", nss_ctx, dynamic_if_type);
if ((dynamic_if_type < NSS_DYNAMIC_INTERFACE_TYPE_NONE) || (dynamic_if_type >= NSS_DYNAMIC_INTERFACE_TYPE_MAX)) {
nss_warning("%px: Invalid dynamic interface type: %d", nss_ctx, dynamic_if_type);
dev_put(dev);
return -EFAULT;
}
if_num = nss_cmn_get_interface_number_by_dev_and_type(dev, dynamic_if_type);
if (if_num < 0) {
nss_warning("%px: Invalid interface number:%s", nss_ctx, dev_name);
dev_put(dev);
return -EFAULT;
}
nss_info("%px: PPE VP create/destroy for, nss_if_num:%d dev_name:%s dynamic_if_type:%u", nss_ctx, if_num, dev_name, dynamic_if_type);
dev_put(dev);
return if_num;
}
/*
* nss_ppe_vp_tx_msg()
* Transmit a ppe_vp message to NSS FW
*/
nss_tx_status_t nss_ppe_vp_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_ppe_vp_msg *msg)
{
struct nss_cmn_msg *ncm = &msg->cm;
nss_if_num_t if_num = ncm->interface;
/*
* Trace messages.
*/
nss_ppe_vp_log_tx_msg(msg);
/*
* Sanity check the message
*/
if (!((ncm->type == NSS_IF_PPE_PORT_CREATE) || (ncm->type == NSS_IF_PPE_PORT_DESTROY))) {
nss_warning("%px: Invalid message type: %d", nss_ctx, ncm->type);
return NSS_TX_FAILURE;
}
if (!(if_num >= NSS_DYNAMIC_IF_START && (if_num < (NSS_DYNAMIC_IF_START + NSS_MAX_DYNAMIC_INTERFACES)))) {
nss_warning("%px: invalid interface %d", nss_ctx, ncm->interface);
return NSS_TX_FAILURE;
}
return nss_core_send_cmd(nss_ctx, msg, sizeof(*msg), NSS_NBUF_PAYLOAD_SIZE);
}
/*
* nss_ppe_vp_tx_msg_sync()
* Transmit a ppe_vp message to NSS firmware synchronously.
*/
nss_tx_status_t nss_ppe_vp_tx_msg_sync(struct nss_ctx_instance *nss_ctx, struct nss_ppe_vp_msg *npvm)
{
nss_tx_status_t status;
int ret = 0;
down(&ppe_vp_pvt.sem);
status = nss_ppe_vp_tx_msg(nss_ctx, npvm);
if (status != NSS_TX_SUCCESS) {
nss_warning("%px: ppe_tx_msg failed", nss_ctx);
up(&ppe_vp_pvt.sem);
return status;
}
ret = wait_for_completion_timeout(&ppe_vp_pvt.complete, msecs_to_jiffies(NSS_PPE_VP_TX_TIMEOUT));
if (!ret) {
nss_warning("%px: ppe_vp msg tx failed due to timeout", nss_ctx);
ppe_vp_pvt.response = NSS_TX_FAILURE;
}
status = ppe_vp_pvt.response;
up(&ppe_vp_pvt.sem);
return status;
}
/*
* nss_ppe_vp_get_context()
* Get NSS context instance for ppe_vp
*/
struct nss_ctx_instance *nss_ppe_vp_get_context(void)
{
return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.ppe_handler_id];
}
EXPORT_SYMBOL(nss_ppe_vp_get_context);
/*
* nss_ppe_vp_get_ppe_port_by_nssif()
* Get vp number for a given NSS interface number.
*/
nss_ppe_port_t nss_ppe_vp_get_ppe_port_by_nssif(struct nss_ctx_instance *nss_ctx, nss_if_num_t if_num)
{
uint32_t idx;
nss_ppe_port_t ppe_port_num;
if (!((if_num >= NSS_DYNAMIC_IF_START) && (if_num < (NSS_DYNAMIC_IF_START + NSS_MAX_DYNAMIC_INTERFACES)))) {
nss_warning("%px: NSS invalid nss if num: %u", nss_ctx, if_num);
return -1;
}
idx = nss_ppe_vp_get_map_index(if_num);
if (idx < 0 || idx >= NSS_MAX_DYNAMIC_INTERFACES) {
nss_warning("%px: NSS invalid index: %d nss if num: %u",nss_ctx, idx, if_num);
return -1;
}
spin_lock_bh(&nss_ppe_vp_map_lock);
if (!vp_map[idx]) {
spin_unlock_bh(&nss_ppe_vp_map_lock);
nss_warning("%px: NSS interface and VP mapping is not present for nss if num: %u",nss_ctx, if_num);
return -1;
}
ppe_port_num = vp_map[idx]->ppe_port_num;
spin_unlock_bh(&nss_ppe_vp_map_lock);
nss_info("%px: VP num %d nss_if: %d",nss_ctx, ppe_port_num, if_num);
return ppe_port_num;
}
EXPORT_SYMBOL(nss_ppe_vp_get_ppe_port_by_nssif);
/*
* nss_ppe_vp_destroy()
* Destroy PPE virtual port for the given nss interface number.
*/
nss_tx_status_t nss_ppe_vp_destroy(struct nss_ctx_instance *nss_ctx, nss_if_num_t if_num)
{
nss_tx_status_t status;
struct nss_ppe_vp_msg *npvm;
uint32_t idx;
int32_t vsi_id_valid = false;
int32_t vsi_id;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
idx = nss_ppe_vp_get_map_index(if_num);
if (idx < 0 || idx >= NSS_MAX_DYNAMIC_INTERFACES) {
nss_warning("%px: Cannot destroy PPE VP. Invalid index: %d. nss_if_num: %u", nss_ctx, idx, if_num);
return -1;
}
spin_lock_bh(&nss_ppe_vp_map_lock);
if (vp_map[idx]) {
vsi_id = vp_map[idx]->vsi_id;
vsi_id_valid = vp_map[idx]->vsi_id_valid;
}
spin_unlock_bh(&nss_ppe_vp_map_lock);
if (vsi_id_valid) {
/*
* Send the dettach VSI message to the Firmware.
*/
if (nss_if_vsi_unassign(nss_ctx, if_num, vsi_id)) {
nss_warning("%px: PPE VP destroy failed. Failed to detach VSI to PPE VP interface %d vsi:%d", nss_ctx, if_num, vsi_id);
return NSS_TX_FAILURE;
}
if (ppe_vsi_free(NSS_PPE_VP_SWITCH_ID, vsi_id)) {
nss_warning("%px: PPE VP destroy failed. Failed to free PPE VSI. nss_if:%d vsi:%d", nss_ctx, if_num, vsi_id);
return NSS_TX_FAILURE;
}
nss_info("%px: PPE VP VSI detached successfully. VSI ID freed successfully. NSS if num:%u, VSI ID:%u", nss_ctx, if_num, vsi_id);
}
npvm = kzalloc(sizeof(struct nss_ppe_vp_msg), GFP_KERNEL);
if (!npvm) {
nss_warning("%px: Unable to allocate memeory of PPE VP message", nss_ctx);
return NSS_TX_FAILURE;
}
nss_trace("%px: PPE_VP will be destroyed for an interface: %d", nss_ctx, if_num);
/*
* Destroy PPE VP for a dynamic interface.
*/
nss_cmn_msg_init(&npvm->cm, if_num, NSS_IF_PPE_PORT_DESTROY, 0, nss_ppe_vp_callback, NULL);
status = nss_ppe_vp_tx_msg_sync(nss_ctx, npvm);
if (status != NSS_TX_SUCCESS) {
nss_warning("%px: Unable to send PPE VP destroy message", nss_ctx);
kfree(npvm);
return NSS_TX_FAILURE;
}
kfree(npvm);
/*
* Delete mapping between the NSS interface number and the VP number.
*/
if (!nss_ppe_vp_del_map(nss_ctx, if_num)) {
nss_warning("%px: Failed to delete the mapping for nss_if:%d", nss_ctx, if_num);
return NSS_TX_FAILURE;
}
return status;
}
EXPORT_SYMBOL(nss_ppe_vp_destroy);
/*
* nss_ppe_vp_create()
* Create PPE virtual port for the given nss interface number.
*/
nss_tx_status_t nss_ppe_vp_create(struct nss_ctx_instance *nss_ctx, nss_if_num_t if_num)
{
uint32_t vsi_id;
nss_tx_status_t status;
struct nss_ppe_vp_msg *npvm;
struct nss_vp_mapping *nss_vp_info;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
/*
* TODO: No need to create VSI for tunnel interfaces. Only for VAP interfaces VSI is needed.
* Allocate the VSI for the dynamic interface on which VP will be created.
*/
if (ppe_vsi_alloc(NSS_PPE_VP_SWITCH_ID, &vsi_id)) {
nss_warning("%px, Failed to alloc VSI ID, PPE VP create failed. nss_if:%u", nss_ctx, if_num);
return NSS_TX_FAILURE;
}
npvm = kzalloc(sizeof(struct nss_ppe_vp_msg), GFP_KERNEL);
if (!npvm) {
nss_warning("%px: Unable to allocate memeory of PPE VP message", nss_ctx);
goto free_vsi;
}
nss_trace("%px: PPE_VP will be created for an interface: %d", nss_ctx, if_num);
/*
* Create PPE VP for a dynamic interface.
*/
nss_cmn_msg_init(&npvm->cm, if_num, NSS_IF_PPE_PORT_CREATE,
sizeof(struct nss_if_ppe_port_create), nss_ppe_vp_callback, NULL);
status = nss_ppe_vp_tx_msg_sync(nss_ctx, npvm);
if (status != NSS_TX_SUCCESS) {
nss_warning("%px: Unable to send ppe_vp create message", nss_ctx);
goto free_alloc;
}
/*
* Send the attach VSI message to the Firmware.
*/
if (nss_if_vsi_assign(nss_ctx, if_num, vsi_id) != NSS_TX_SUCCESS) {
nss_warning("%px: Failed to attach VSI to PPE VP interface. nss_if:%u vsi:%u", nss_ctx, if_num, vsi_id);
goto destroy_vp;
}
nss_vp_info = nss_ppe_vp_map_alloc();
if (!nss_vp_info) {
nss_warning("%px: No memory for allocating NSS-VP mapping instance", nss_ctx);
goto detach_vsi;
}
nss_vp_info->vsi_id = vsi_id;
nss_vp_info->vsi_id_valid = true;
nss_vp_info->if_num = if_num;
nss_vp_info->ppe_port_num = ppe_vp_pvt.ppe_port_num;
nss_info("%px: PPE VP allocated VSI ID:%u NSS interface number:%u VP no from Firmware:%u", nss_ctx, vsi_id, if_num, nss_vp_info->ppe_port_num);
/*
* Add mapping between the NSS interface number and the VP number.
*/
if (!nss_ppe_vp_add_map(nss_ctx, if_num, nss_vp_info)) {
nss_warning("%px: Failed to add mapping for NSS interface number: %d", nss_ctx, if_num);
goto free_nss_vp_info;
}
kfree(npvm);
return status;
free_nss_vp_info:
nss_ppe_vp_map_dealloc(nss_vp_info);
detach_vsi:
nss_trace("%px: Detaching VSI ID :%u NSS Interface no:%u", nss_ctx, vsi_id, if_num);
if (nss_if_vsi_unassign(nss_ctx, if_num, vsi_id)) {
nss_warning("%px: Failed to free PPE VP VSI. nss_if:%u vsi:%u", nss_ctx, if_num, vsi_id);
}
destroy_vp:
nss_trace("%px: Destroy Vp for NSS Interface num:%u VP num:%u", nss_ctx, if_num, npvm->msg.if_msg.ppe_port_create.ppe_port_num);
if (nss_ppe_vp_destroy(nss_ctx, if_num)) {
nss_warning("%px: PPE VP destroy failed, nss_if:%u", nss_ctx, if_num);
}
free_alloc:
kfree(npvm);
free_vsi:
nss_trace("%px: Free VSI ID :%u NSS Interface no:%u", nss_ctx, vsi_id, if_num);
if (ppe_vsi_free(NSS_PPE_VP_SWITCH_ID, vsi_id)) {
nss_warning("%px: Failed to free PPE VP VSI. NSS if num:%u vsi:%u", nss_ctx, if_num, vsi_id);
}
return NSS_TX_FAILURE;
}
EXPORT_SYMBOL(nss_ppe_vp_create);
/*
* nss_ppe_vp_destroy_notify()
* Get PPE VP destroy notification from NSS
*/
static void nss_ppe_vp_destroy_notify(struct nss_ctx_instance *nss_ctx, struct nss_ppe_vp_destroy_notify_msg *destroy_notify)
{
nss_if_num_t nss_if_num;
uint32_t i;
int32_t vsi_id;
bool vsi_id_valid = false;
nss_ppe_port_t ppe_port_num = destroy_notify->ppe_port_num;
/*
* Find NSS interface number corresponding to the VP num.
*/
spin_lock_bh(&nss_ppe_vp_map_lock);
for (i = 0; i < NSS_MAX_DYNAMIC_INTERFACES; i++) {
if (vp_map[i] && (ppe_port_num == vp_map[i]->ppe_port_num)) {
nss_if_num = vp_map[i]->if_num;
vsi_id = vp_map[i]->vsi_id;
vsi_id_valid = vp_map[i]->vsi_id_valid;
break;
}
}
spin_unlock_bh(&nss_ppe_vp_map_lock);
if (i == NSS_MAX_DYNAMIC_INTERFACES) {
nss_warning("%px: Could not find the NSS interface number mapping for VP number: %u\n", nss_ctx, ppe_port_num);
return;
}
/*
* Delete the nss_if_num to VP num mapping and reset the stats entry for this VP.
*/
if (!nss_ppe_vp_del_map(nss_ctx, nss_if_num)) {
nss_warning("%px: Failed to delete the mapping for nss_if: %d\n", nss_ctx, nss_if_num);
return;
}
if (vsi_id_valid && ppe_vsi_free(NSS_PPE_VP_SWITCH_ID, vsi_id)) {
nss_warning("%px: Failed to free PPE VSI. nss_if: %d vsi: %d\n", nss_ctx, nss_if_num, vsi_id);
}
}
/*
* nss_ppe_vp_handler()
* Handle NSS -> HLOS messages for ppe
*/
static void nss_ppe_vp_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data)
{
struct nss_ppe_vp_msg *msg = (struct nss_ppe_vp_msg *)ncm;
nss_ppe_vp_msg_callback_t cb;
void *ctx;
NSS_VERIFY_CTX_MAGIC(nss_ctx);
nss_trace("%px ppe_vp msg: %px\n", nss_ctx, msg);
BUG_ON(!nss_ppe_vp_verify_ifnum(ncm->interface));
/*
* Is this a valid request/response packet?
*/
if (ncm->type >= NSS_PPE_VP_MSG_MAX) {
nss_warning("%px: received invalid message %d for PPE_VP interface", nss_ctx, ncm->type);
return;
}
if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_ppe_vp_msg)) {
nss_warning("%px: Length of message is greater than required: %d", nss_ctx, nss_cmn_get_msg_len(ncm));
return;
}
/*
* Trace messages.
*/
nss_ppe_vp_log_rx_msg(msg);
switch (msg->cm.type) {
case NSS_PPE_VP_MSG_SYNC_STATS:
/*
* Per VP stats msg
*/
nss_ppe_vp_stats_sync(nss_ctx, &msg->msg.stats, ncm->interface);
break;
case NSS_PPE_VP_MSG_DESTROY_NOTIFY:
/*
* VP destroy notification
*/
nss_ppe_vp_destroy_notify(nss_ctx, &msg->msg.destroy_notify);
break;
}
if (ncm->response == NSS_CMN_RESPONSE_NOTIFY) {
ncm->cb = (nss_ptr_t)nss_core_get_msg_handler(nss_ctx, ncm->interface);
ncm->app_data = (nss_ptr_t)nss_ctx->nss_rx_interface_handlers[ncm->interface].app_data;
}
/*
* Log failures
*/
nss_core_log_msg_failures(nss_ctx, ncm);
/*
* Do we have a call back
*/
if (!ncm->cb) {
return;
}
/*
* Callback
*/
cb = (nss_ppe_vp_msg_callback_t)ncm->cb;
ctx = (void *)ncm->app_data;
cb(ctx, msg);
}
/*
* nss_ppe_vp_destroy_handler()
* PPE VP destroy handler.
*/
static int nss_ppe_vp_destroy_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
{
struct nss_ctx_instance *nss_ctx = nss_ppe_vp_get_context();
int32_t if_num;
nss_tx_status_t nss_tx_status;
if (!nss_ctx) {
nss_warning("%px: NSS Context not found.", nss_ctx);
return -ENODEV;
}
NSS_VERIFY_CTX_MAGIC(nss_ctx);
if_num = nss_ppe_vp_parse_vp_cmd(ctl, write, buffer, lenp, ppos);
if (if_num < 0) {
nss_warning("%px: Invalid interface number: %d", nss_ctx, if_num);
return -EFAULT;
}
if (nss_ppe_vp_get_ppe_port_by_nssif(nss_ctx, if_num) < 0) {
nss_warning("%px: VP is not present for interface: %d", nss_ctx, if_num);
return -EEXIST;
}
nss_tx_status = nss_ppe_vp_destroy(nss_ctx, if_num);
if (nss_tx_status != NSS_TX_SUCCESS) {
nss_warning("%px: Sending message failed, cannot destroy PPE_VP node nss_if: %u", nss_ctx, if_num);
return -EBUSY;
}
return 0;
}
/*
* nss_ppe_vp_create_handler()
* PPE VP create handler.
*/
static int nss_ppe_vp_create_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
{
int32_t if_num;
struct nss_ctx_instance *nss_ctx = nss_ppe_vp_get_context();
nss_tx_status_t nss_tx_status;
if (!nss_ctx) {
nss_warning("%px: NSS Context not found.", nss_ctx);
return -ENODEV;
}
NSS_VERIFY_CTX_MAGIC(nss_ctx);
if_num = nss_ppe_vp_parse_vp_cmd(ctl, write, buffer, lenp, ppos);
if (if_num < 0) {
nss_warning("%px: Invalid interface number: %d", nss_ctx, if_num);
return -EFAULT;
}
nss_info("%px: NSS interface number: %d", nss_ctx, if_num);
if (nss_ppe_vp_get_ppe_port_by_nssif(nss_ctx, if_num) > 0) {
nss_warning("%px: VP is already present for nss_if_num: %d", nss_ctx, if_num);
return -EEXIST;
}
nss_tx_status = nss_ppe_vp_create(nss_ctx, if_num);
if (nss_tx_status != NSS_TX_SUCCESS) {
nss_warning("%px: Sending message failed, cannot create PPE VP node for nss_if_num: %u", nss_ctx, if_num);
return -EBUSY;
}
return 0;
}
static struct ctl_table nss_ppe_vp_table[] = {
{
.procname = "create",
.data = &nss_ppe_vp_cmd,
.maxlen = sizeof(nss_ppe_vp_cmd),
.mode = 0644,
.proc_handler = &nss_ppe_vp_create_handler,
},
{
.procname = "destroy",
.data = &nss_ppe_vp_cmd,
.maxlen = sizeof(nss_ppe_vp_cmd),
.mode = 0644,
.proc_handler = &nss_ppe_vp_destroy_handler,
},
{ }
};
static struct ctl_table nss_ppe_vp_dir[] = {
{
.procname = "ppe_vp",
.mode = 0555,
.child = nss_ppe_vp_table,
},
{ }
};
static struct ctl_table nss_ppe_vp_root_dir[] = {
{
.procname = "nss",
.mode = 0555,
.child = nss_ppe_vp_dir,
},
{ }
};
static struct ctl_table_header *nss_ppe_vp_procfs_header;
/*
* nss_ppe_vp_procfs_register()
* Register sysctl specific to ppe_vp
*/
void nss_ppe_vp_procfs_register(void)
{
/*
* Register sysctl table.
*/
nss_ppe_vp_procfs_header = register_sysctl_table(nss_ppe_vp_root_dir);
}
/*
* uss_ppe_vp_procfs_unregister()
* Unregister sysctl specific for ppe_vp
*/
void nss_ppe_vp_procfs_unregister(void)
{
/*
* Unregister sysctl table.
*/
if (nss_ppe_vp_procfs_header) {
unregister_sysctl_table(nss_ppe_vp_procfs_header);
}
}
/*
* nss_ppe_vp_register_handler()
*
*/
void nss_ppe_vp_register_handler(void)
{
struct nss_ctx_instance *nss_ctx = nss_ppe_vp_get_context();
nss_ppe_vp_dentry = nss_ppe_vp_stats_dentry_create();
if (nss_ppe_vp_dentry == NULL) {
nss_warning("%px: Not able to create debugfs entry", nss_ctx);
return;
}
nss_core_register_handler(nss_ctx, NSS_PPE_VP_INTERFACE, nss_ppe_vp_handler, NULL);
nss_ppe_vp_procfs_register();
sema_init(&ppe_vp_pvt.sem, 1);
init_completion(&ppe_vp_pvt.complete);
}
/*
* nss_ppe_vp_unregister_handler()
*
*/
void nss_ppe_vp_unregister_handler(void)
{
struct nss_ctx_instance *nss_ctx = nss_ppe_vp_get_context();
debugfs_remove_recursive(nss_ppe_vp_dentry);
nss_ppe_vp_procfs_unregister();
nss_core_unregister_handler(nss_ctx, NSS_PPE_VP_INTERFACE);
}