blob: e88e7c668e520202917bc01d6151f0dc03eeb20f [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.
**************************************************************************
*/
/*
* nss_dma.c
* NSS DMA APIs
*/
#include <nss_hal.h>
#include "nss_dma_stats.h"
#include "nss_dma_log.h"
#include "nss_dma_strings.h"
/*
* Test configuration value
*/
struct nss_dma_test_cfg_val {
int val; /* field value */
int min; /* Minimum value */
int max; /* Maximum value */
};
/*
* Test configuration for user
*/
struct nss_dma_test_cfg_user {
struct nss_dma_test_cfg_val run; /* test run state */
struct nss_dma_test_cfg_val code; /* test run code */
struct nss_dma_test_cfg_val type; /* test type code */
struct nss_dma_test_cfg_val packets; /* packet count per loop */
int result_tx_packets; /* test results TX packets */
int result_rx_packets; /* test result RX packets */
int result_time; /* test time */
};
static struct nss_dma_test_cfg_user test_cfg = {
.run = {.val = 0, .min = 0 /* stopped */, .max = 1 /* running */},
.code = {.val = 1, .min = 1 /* linearize */, .max = 2 /* split */},
.type = {.val = NSS_DMA_TEST_TYPE_DEFAULT, .min = NSS_DMA_TEST_TYPE_DEFAULT, .max = NSS_DMA_TEST_TYPE_MAX},
.packets = {.val = 1, .min = 1, .max = 65536},
};
/*
* Private data structure.
*/
struct nss_dma_pvt {
struct semaphore sem; /* Semaphore structure. */
struct completion complete; /* Completion structure. */
int response; /* Response from FW. */
void *cb; /* Original cb for sync msgs. */
void *app_data; /* Original app_data for sync msgs. */
};
static struct nss_dma_pvt nss_dma_cfg_pvt;
/*
* nss_dma_verify_if_num()
* Verify if_num passed to us.
*/
static inline bool nss_dma_verify_if_num(uint32_t if_num)
{
return if_num == NSS_DMA_INTERFACE;
}
/*
* nss_dma_interface_handler()
* Handle NSS -> HLOS messages for DMA Statistics
*/
static void nss_dma_msg_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm,
__attribute__((unused))void *app_data)
{
struct nss_dma_msg *ndm = (struct nss_dma_msg *)ncm;
nss_dma_msg_callback_t cb;
if (!nss_dma_verify_if_num(ncm->interface)) {
nss_warning("%px: invalid interface %d for dma\n", nss_ctx, ncm->interface);
return;
}
/*
* Is this a valid request/response packet?
*/
if (ncm->type >= NSS_DMA_MSG_TYPE_MAX) {
nss_warning("%px: received invalid message %d for dma", nss_ctx, ncm->type);
return;
}
if (nss_cmn_get_msg_len(ncm) > sizeof(struct nss_dma_msg)) {
nss_warning("%px: Length of message is greater than required: %d", nss_ctx, nss_cmn_get_msg_len(ncm));
return;
}
/*
* Trace messages.
*/
nss_dma_log_rx_msg(ndm);
/*
* Log failures
*/
nss_core_log_msg_failures(nss_ctx, ncm);
/*
* Update driver statistics and send statistics notifications to the registered modules.
*/
if (ndm->cm.type == NSS_DMA_MSG_TYPE_SYNC_STATS) {
nss_dma_stats_sync(nss_ctx, &ndm->msg.stats);
nss_dma_stats_notify(nss_ctx);
}
/*
* Update the callback and app_data for NOTIFY messages
*/
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;
}
/*
* Do we have a callback?
*/
if (!ncm->cb) {
return;
}
/*
* callback
*/
cb = (nss_dma_msg_callback_t)ncm->cb;
cb((void *)ncm->app_data, ncm);
}
/*
* nss_dma_register_handler()
* Register handler for messaging
*/
void nss_dma_register_handler(void)
{
struct nss_ctx_instance *nss_ctx = nss_dma_get_context();
nss_info("%px: nss_dma_register_handler", nss_ctx);
nss_core_register_handler(nss_ctx, NSS_DMA_INTERFACE, nss_dma_msg_handler, NULL);
nss_dma_stats_dentry_create();
nss_dma_strings_dentry_create();
}
EXPORT_SYMBOL(nss_dma_register_handler);
/*
* nss_dma_tx_msg()
* Transmit an dma message to the FW with a specified size.
*/
nss_tx_status_t nss_dma_tx_msg(struct nss_ctx_instance *nss_ctx, struct nss_dma_msg *ndm)
{
struct nss_cmn_msg *ncm = &ndm->cm;
/*
* Sanity check the message
*/
if (!nss_dma_verify_if_num(ncm->interface)) {
nss_warning("%px: tx request for another interface: %d", nss_ctx, ncm->interface);
return NSS_TX_FAILURE;
}
if (ncm->type >= NSS_DMA_MSG_TYPE_MAX) {
nss_warning("%px: message type out of range: %d", nss_ctx, ncm->type);
return NSS_TX_FAILURE;
}
/*
* Trace messages.
*/
nss_dma_log_tx_msg(ndm);
return nss_core_send_cmd(nss_ctx, ndm, sizeof(*ndm), NSS_NBUF_PAYLOAD_SIZE);
}
EXPORT_SYMBOL(nss_dma_tx_msg);
/*
* nss_dma_msg_test_callback()
* Callback function for dma test start configuration
*/
static void nss_dma_msg_test_callback(void *app_data, struct nss_cmn_msg *ncm)
{
struct nss_ctx_instance *nss_ctx __attribute__((unused)) = (struct nss_ctx_instance *)app_data;
struct nss_dma_msg *ndm = (struct nss_dma_msg *)ncm;
struct nss_dma_test_cfg *ndtc = &ndm->msg.test_cfg;
struct nss_cmn_node_stats *ncns = &ndtc->node_stats;
test_cfg.run.val = 0; /* test completed */
/*
* Test start has been failed. Restore the value to initial state.
*/
if (ndm->cm.response != NSS_CMN_RESPONSE_ACK) {
nss_warning("%px: nss dma test failed: %d \n", nss_ctx, ndm->cm.error);
test_cfg.result_tx_packets = 0;
test_cfg.result_rx_packets = 0;
test_cfg.result_time = 0;
return;
}
test_cfg.result_tx_packets = ncns->tx_packets;
test_cfg.result_rx_packets = ncns->rx_packets;
test_cfg.result_time = ndtc->time_delta;
nss_info("%px: nss dma test complete\n", nss_ctx);
nss_info("%px: results tx=%u, rx=%u, time=%u\n", ndm, ncns->tx_packets, ncns->rx_packets, ndtc->time_delta);
}
/*
* nss_dma_msg_test()
* Send NSS DMA test start message.
*/
static nss_tx_status_t nss_dma_msg_test(struct nss_ctx_instance *nss_ctx)
{
struct nss_dma_msg ndm;
uint32_t flags = 0;
int32_t status;
size_t len;
len = sizeof(struct nss_cmn_msg) + sizeof(struct nss_dma_test_cfg);
nss_info("%px: DMA test message:%x\n", nss_ctx, test_cfg.run.val);
if (test_cfg.code.val == 1) {
flags = NSS_DMA_TEST_FLAGS_LINEARIZE;
}
nss_dma_msg_init(&ndm, NSS_DMA_INTERFACE, NSS_DMA_MSG_TYPE_TEST_PERF, len, nss_dma_msg_test_callback, nss_ctx);
ndm.msg.test_cfg.packet_count = test_cfg.packets.val;
ndm.msg.test_cfg.type = test_cfg.type.val;
ndm.msg.test_cfg.flags = flags;
status = nss_dma_tx_msg(nss_ctx, &ndm);
if (unlikely(status != NSS_TX_SUCCESS)) {
return status;
}
/*
* Test is now running
*/
test_cfg.run.val = 1;
return NSS_TX_SUCCESS;
}
/*
* nss_dma_msg_init()
* Initialize DMA message.
*/
void nss_dma_msg_init(struct nss_dma_msg *ndm, uint16_t if_num, uint32_t type, uint32_t len, nss_dma_msg_callback_t cb,
void *app_data)
{
nss_cmn_msg_init(&ndm->cm, if_num, type, len, (void *)cb, app_data);
}
EXPORT_SYMBOL(nss_dma_msg_init);
/*
* nss_crypto_cmn_get_context()
* get NSS context instance for crypto handle
*/
struct nss_ctx_instance *nss_dma_get_context(void)
{
return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.dma_handler_id];
}
EXPORT_SYMBOL(nss_dma_get_context);
/*
* nss_dma_test_handler()
* Handles the performance test.
*/
static int nss_dma_test_handler(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
{
struct nss_ctx_instance *nss_ctx = nss_dma_get_context();
int cur_state = test_cfg.run.val;
int ret;
ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
if (ret != NSS_SUCCESS) {
return ret;
}
if (!write) {
return ret;
}
/*
* Check any tests are already scheduled
*/
if (cur_state > 0) {
nss_info("%px: Test is already running, stopping it.\n", nss_ctx);
} else {
nss_info("%px: Test is not running, starting it.\n", nss_ctx);
}
ret = nss_dma_msg_test(nss_ctx);
if (ret != NSS_SUCCESS) {
nss_warning("%px: Test configuration has failed.\n", nss_ctx);
test_cfg.run.val = 0;
}
return ret;
}
static struct ctl_table nss_dma_table[] = {
{
.procname = "test_run",
.data = &test_cfg.run.val,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = nss_dma_test_handler,
.extra1 = &test_cfg.run.min,
.extra2 = &test_cfg.run.max,
},
{
.procname = "test_code",
.data = &test_cfg.code.val,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &test_cfg.code.min,
.extra2 = &test_cfg.code.max,
},
{
.procname = "test_type",
.data = &test_cfg.type.val,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &test_cfg.type.min,
.extra2 = &test_cfg.type.max,
},
{
.procname = "test_packets",
.data = &test_cfg.packets.val,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &test_cfg.packets.min,
.extra2 = &test_cfg.packets.max,
},
{
.procname = "result_tx",
.data = &test_cfg.result_tx_packets,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
.procname = "result_rx",
.data = &test_cfg.result_rx_packets,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
.procname = "result_time",
.data = &test_cfg.result_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
{ }
};
static struct ctl_table nss_dma_dir[] = {
{
.procname = "dma",
.mode = 0555,
.child = nss_dma_table,
},
{ }
};
static struct ctl_table nss_dma_root_dir[] = {
{
.procname = "nss",
.mode = 0555,
.child = nss_dma_dir,
},
{ }
};
static struct ctl_table nss_dma_root[] = {
{
.procname = "dev",
.mode = 0555,
.child = nss_dma_root_dir,
},
{ }
};
static struct ctl_table_header *nss_dma_header;
/*
* nss_dma_register_sysctl()
*/
void nss_dma_register_sysctl(void)
{
/*
* dma sema init.
*/
sema_init(&nss_dma_cfg_pvt.sem, 1);
init_completion(&nss_dma_cfg_pvt.complete);
/*
* Register sysctl table.
*/
nss_dma_header = register_sysctl_table(nss_dma_root);
}
/*
* nss_dma_unregister_sysctl()
* Unregister sysctl specific to dma
*/
void nss_dma_unregister_sysctl(void)
{
/*
* Unregister sysctl table.
*/
if (nss_dma_header) {
unregister_sysctl_table(nss_dma_header);
}
}
/*
* nss_dma_notify_register()
* Register to receive dma notify messages.
*/
struct nss_ctx_instance *nss_dma_notify_register(int core, nss_dma_msg_callback_t cb, void *app_data)
{
struct nss_ctx_instance *nss_ctx = nss_dma_get_context();
uint32_t ret;
ret = nss_core_register_handler(nss_ctx, NSS_DMA_INTERFACE, nss_dma_msg_handler, app_data);
if (ret != NSS_CORE_STATUS_SUCCESS) {
nss_warning("%px: unable to register event handler for DMA interface", nss_ctx);
return NULL;
}
ret = nss_core_register_msg_handler(nss_ctx, NSS_DMA_INTERFACE, cb);
if (ret != NSS_CORE_STATUS_SUCCESS) {
nss_core_unregister_handler(nss_ctx, NSS_DMA_INTERFACE);
nss_warning("%px: unable to register event handler for DMA interface", nss_ctx);
return NULL;
}
return nss_ctx;
}
EXPORT_SYMBOL(nss_dma_notify_register);
/*
* nss_dma_notify_unregister()
* Unregister to receive dma notify messages.
*/
void nss_dma_notify_unregister(int core)
{
struct nss_ctx_instance *nss_ctx = nss_dma_get_context();
uint32_t ret;
BUG_ON(!nss_ctx);
ret = nss_core_unregister_msg_handler(nss_ctx, NSS_DMA_INTERFACE);
if (ret != NSS_CORE_STATUS_SUCCESS) {
nss_warning("%px: unable to unregister event handler for DMA interface", nss_ctx);
return;
}
ret = nss_core_unregister_handler(nss_ctx, NSS_DMA_INTERFACE);
if (ret != NSS_CORE_STATUS_SUCCESS) {
nss_warning("%px: unable to unregister event handler for DMA interface", nss_ctx);
return;
}
return;
}
EXPORT_SYMBOL(nss_dma_notify_unregister);
/*
* nss_dma_init()
*/
void nss_dma_init(void)
{
nss_dma_register_sysctl();
}