M7350/qcom-opensource/kernel/kernel-tests/glink/glink_unit_test.c
2024-09-09 08:57:42 +00:00

5629 lines
165 KiB
C

/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/ipc_logging.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/ctype.h>
#include <soc/qcom/glink.h>
#include <soc/qcom/glink_rpm_xprt.h>
#include <soc/qcom/smsm.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/tracer_pkt.h>
#include "glink_ch_migration_test.h"
#include "glink_core_if.h"
#include "glink_loopback_client.h"
#include "glink_mock_xprt.h"
#include "glink_private.h"
#include "glink_test_common.h"
#define MAX_PERF_ITERATIONS 20
static atomic_t local_mt_test_num_workers = ATOMIC_INIT(0);
static atomic_t mpss_mt_test_num_workers = ATOMIC_INIT(0);
static int ut_iterations = 5;
module_param_named(iterations, ut_iterations,
int, S_IRUGO | S_IWUSR | S_IWGRP);
static int ut_vector_buf_count = 1;
module_param_named(vector_buf_count, ut_vector_buf_count,
int, S_IRUGO | S_IWUSR | S_IWGRP);
static int ut_tracer_pkt_size = 256;
module_param_named(tracer_pkt_size, ut_tracer_pkt_size,
int, S_IRUGO | S_IWUSR | S_IWGRP);
/* Begin - Module parameters to configure multi-threaded test parameters */
/* Interval between two packet transmission by a latency-specific client */
static int ut_mt_lat_pkt_ntrvl_ms = 20;
module_param_named(pkt_interval_ms, ut_mt_lat_pkt_ntrvl_ms,
int, S_IRUGO | S_IWUSR | S_IWGRP);
/* Number of latency-sensitive clients */
static int ut_mt_num_lat_clnts = 1;
module_param_named(num_lat_clnts, ut_mt_num_lat_clnts,
int, S_IRUGO | S_IWUSR | S_IWGRP);
/* Number of throughput-sensitive clients */
static int ut_mt_num_tput_clnts = 1;
module_param_named(num_tput_clnts, ut_mt_num_tput_clnts,
int, S_IRUGO | S_IWUSR | S_IWGRP);
/* Number of packets to be transmitted by a latency-sensitive client */
static int ut_mt_num_pkts_lat_ch = 100;
module_param_named(num_pkts_lat_ch, ut_mt_num_pkts_lat_ch,
int, S_IRUGO | S_IWUSR | S_IWGRP);
/* Number of packets to be transmitted by a throughput-sensitive client */
static int ut_mt_num_pkts_tput_ch = 100;
module_param_named(num_pkts_tput_ch, ut_mt_num_pkts_tput_ch,
int, S_IRUGO | S_IWUSR | S_IWGRP);
/* Size of a packet transmitted by a latency-sensitive client */
static int ut_mt_pkt_size_lat_ch = 128;
module_param_named(pkt_size_lat_ch, ut_mt_pkt_size_lat_ch,
int, S_IRUGO | S_IWUSR | S_IWGRP);
/* Size of a packet transmitted by a throughput-sensitive client */
static int ut_mt_pkt_size_tput_ch = 512;
module_param_named(pkt_size_tput_ch, ut_mt_pkt_size_tput_ch,
int, S_IRUGO | S_IWUSR | S_IWGRP);
/* End - Module parameters to configure multi-threaded test parameters */
const char *testdata = "Hello GLINK";
static struct workqueue_struct *ut_intent_req_workqueue;
struct mutex multithread_test_mutex_lha0;
static void *glink_ut_link_state_notif_handle;
static struct workqueue_struct *ut_dbgfs_create_workqueue;
struct glink_ut_dbgfs_work {
const char *edge;
const char *xprt;
struct work_struct dbgfs_work;
};
/**
* Structure to hold status of TX work function
* @glink_tx_entered: Holds status about that the work function is called.
* @glink_tx_exited: Status of work function executed or not.
*/
struct ut_tx_stat {
bool glink_tx_entered;
bool glink_tx_exited;
};
/**
* struct ut_intent_req_tx_work - TX work structure
* @handle: Pointer to channel context
* @pkt_priv: Opague data value
* @data: Pointer to data
* @size: Size of data
* req_intent: Request for rx_intent from remote side
* @work: Work to be excuted
* @tx_stat: Instance of struct ut_tx_stat
*/
struct ut_intent_req_tx_work {
void *handle;
void *pkt_priv;
void *data;
size_t size;
bool req_intent;
struct work_struct work;
struct ut_tx_stat tx_stat;
struct completion tx_worker_complete;
};
/**
* struct ch_config - Channel Configuration in a multi-client test
* @ch_name: Name of the channel to be used by the client
* @num_pkts: Number of packets to be transmitted by the client
* @data_len: Packet size to be transmitted by the client
* @tx_delay_ms: Time delay before the server echoes back the data
* @pkt_ntrvl: Interval between transmission by the client
*/
struct ch_config {
char ch_name[GLINK_NAME_SIZE];
uint32_t num_pkts;
size_t data_len;
uint32_t tx_delay_ms;
uint32_t pkt_ntrvl;
};
/**
* struct mt_test_info_struct - Multithreaded test information structure
* @s: Pointer to output file
* @pkt: Loopback server request structure
* @cntl_handle: Pointer to control channel handle
* @data_handle: Pointer to data channel handle
* @name: Data channel name
* @work: Work to be excuted
* @waitqueue: Waitqueue structure for thread tracking
* @result: Used to indicate test failure from within the worker function
* @num_workers: The number of worker threads/clients still running
* @ch_cfg: Channel Configuration to be used by a client/worker thread
* @index: Index/Identity of the client/worker thread
*/
struct mt_test_info_struct {
struct seq_file *s;
struct req pkt;
void *cntl_handle;
void *data_handle;
char name[GLINK_NAME_SIZE];
struct work_struct work;
wait_queue_head_t *waitqueue;
int *result;
atomic_t *num_workers;
struct ch_config ch_cfg;
int index;
};
static int glink_ut1_local_mt_send_config_reqs(struct seq_file *s, void
*cntl_handle, const char *name);
static int glink_ut1_mpss_mt_send_config_reqs(struct seq_file *s, void
*cntl_handle, const char *name,
struct mt_test_info_struct *mpss_mt_thread_info);
static void ut_local_mt_worker_func(struct work_struct *work);
static void ut_mpss_mt_worker_func(struct work_struct *work);
static void glink_ut_to_upper(char *out, char *input)
{
int i;
for (i = 0; i < strlen(input); i++)
out[i] = toupper(input[i]);
}
void glink_test_notify_rx(void *handle, const void *priv, const void *pkt_priv,
const void *ptr, size_t size)
{
struct ut_notify_data *cb_data_ptr = (struct ut_notify_data *)priv;
if (!cb_data_ptr) {
GLINK_UT_ERR("%s: Could not allocate data for cb_data_ptr\n",
__func__);
return;
}
GLINK_UT_INFO("%s: priv[%p] data[%p] size[%zu], thread: %d\n", __func__,
pkt_priv, (char *)ptr, size, current->pid);
cb_data_ptr->size = size;
cb_data_ptr->rx_notify = true;
complete(&cb_data_ptr->cb_completion);
}
void glink_test_notify_tx_done(void *handle, const void *priv, const void
*pkt_priv, const void *ptr)
{
struct ut_notify_data *cb_data_ptr = (struct ut_notify_data *)priv;
if (!cb_data_ptr) {
GLINK_UT_ERR("%s: Could not allocate data for cb_data_ptr\n",
__func__);
return;
}
GLINK_UT_INFO("%s: priv[%p] pkt_priv[%p] ptr[%p], thread: %d\n",
__func__,
priv, pkt_priv, ptr, current->pid);
cb_data_ptr->tx_done = true;
}
void glink_test_notify_state(void *handle, const void *priv, unsigned event)
{
struct ut_notify_data *cb_data_ptr = (struct ut_notify_data *)priv;
if (!cb_data_ptr) {
GLINK_UT_ERR("%s: Could not allocate data for cb_data_ptr\n",
__func__);
return;
}
GLINK_UT_INFO("%s: event[%d], thread: %d\n", __func__, event,
current->pid);
cb_data_ptr->event = event;
complete(&cb_data_ptr->cb_completion);
}
#define RMT_RX_INTENT_REQ_SIZE 20
bool glink_test_rmt_rx_intent_req_cb(void *handle, const void *priv, size_t sz)
{
bool ret = false;
struct ut_notify_data *cb_data_ptr = (struct ut_notify_data *)priv;
if (!cb_data_ptr) {
GLINK_UT_ERR("%s: Could not allocate data for cb_data_ptr\n",
__func__);
return -ENOMEM;
}
GLINK_UT_INFO("%s: Triggered with size %zu", __func__, sz);
cb_data_ptr->send_intent = false;
cb_data_ptr->intent_cb_ntfy = true;
if (sz == RMT_RX_INTENT_REQ_SIZE) {
ret = true;
cb_data_ptr->send_intent = ret;
}
return ret;
}
/**
* ut_tx_worker() - Work function to TX data
* @work: Pointer to the work structure
* @returns: void
*
* This function transmits a data packet to the remote side
*/
static void ut_tx_worker(struct work_struct *work)
{
struct ut_intent_req_tx_work *tx_work =
container_of(work, struct ut_intent_req_tx_work, work);
tx_work->tx_stat.glink_tx_entered = true;
glink_tx(tx_work->handle, tx_work->pkt_priv, tx_work->data,
tx_work->size,
(tx_work->req_intent ? GLINK_TX_REQ_INTENT : 0));
tx_work->tx_stat.glink_tx_exited = true;
complete(&tx_work->tx_worker_complete);
}
/**
* glink_ut1_mock_rmt_rx_intent_req - Use mock transport to test
* remote rx intent request handling
* by sending ack and rx intent.
*
* @s: pointer to output file
*
* This test simulates linkup a transport, accept a remote rx intent
* and send acknowledgment to the request. Then the test sends a rx
* intent.
*/
static void glink_ut1_mock_rmt_rx_intent_req(struct seq_file *s)
{
void *handle = NULL;
int failed = 0;
int local_rcid;
int ret;
struct glink_mock_xprt *mock_ptr;
struct glink_mock_cmd *tx_cmd;
struct glink_open_config open_cfg;
struct glink_mock_rx_intent *intent;
struct ut_notify_data cb_data;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
/* Open Glink channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.priv = &cb_data;
cb_data.intent_cb_ntfy = false;
cb_data.send_intent = false;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
local_rcid = tx_cmd->remote_open_ack.rcid;
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* Simulate Remote Intent Request with non-zero size */
mock_ptr->if_ptr.glink_core_if_ptr->
rx_cmd_remote_rx_intent_req(&mock_ptr->if_ptr, local_rcid,
RMT_RX_INTENT_REQ_SIZE);
UT_ASSERT_INT(1, ==, cb_data.intent_cb_ntfy);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_RX_INTENT_ACK, ==, tx_cmd->type);
kfree(tx_cmd);
if (cb_data.send_intent) {
glink_queue_rx_intent(handle, (void *)&cb_data,
RMT_RX_INTENT_REQ_SIZE);
intent = mock_xprt_get_intent(mock_ptr);
UT_ASSERT_PTR(NULL, !=, intent);
UT_ASSERT_INT(intent->size, ==,
RMT_RX_INTENT_REQ_SIZE);
kfree(intent);
}
/* Simulate Remote Intent Request with zero size */
mock_ptr->if_ptr.glink_core_if_ptr->
rx_cmd_remote_rx_intent_req(&mock_ptr->if_ptr,
local_rcid, 0);
UT_ASSERT_INT(1, ==, cb_data.intent_cb_ntfy);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_RX_INTENT_ACK, ==, tx_cmd->type);
kfree(tx_cmd);
UT_ASSERT_INT(0, ==, cb_data.send_intent);
/* Close the Glink Channel */
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_close.lcid);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, tx_cmd->local_close.lcid);
kfree(tx_cmd);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, 1);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
/**
* glink_ut1_mock_rx_intent_req - Use mock transport to test
* local rx intent request handling
* by sending ack and rx intent.
*
* @s: pointer to output file
*
* This test simulates linkup a transport, accept a remote rx intent
* and send acknowledgement to the request. Then the test sends a rx
* intent.
*/
static void glink_ut1_mock_rx_intent_req(struct seq_file *s)
{
void *handle = NULL;
int failed = 0;
int local_rcid;
int ret;
struct glink_mock_xprt *mock_ptr;
struct glink_mock_cmd *tx_cmd;
struct glink_mock_tx_data *tx_data = NULL;
struct glink_open_config open_cfg;
struct ut_intent_req_tx_work *tx_work_ptr;
struct ut_notify_data cb_data;
struct completion event;
GLINK_STATUS(s, "Running %s\n", __func__);
ut_intent_req_workqueue =
create_singlethread_workqueue("ut_intent_req");
if (!ut_intent_req_workqueue) {
GLINK_STATUS(s, "\tfailed to create workqueue\n");
return;
}
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
init_completion(&event);
register_completion(&mock_ptr->if_ptr, &event);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
/* Open Glink channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.priv = &cb_data;
cb_data.intent_cb_ntfy = false;
cb_data.send_intent = false;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
local_rcid = tx_cmd->remote_open_ack.rcid;
kfree(tx_cmd);
/* Populate the G-Link TX context*/
tx_work_ptr = kmalloc(sizeof(struct ut_intent_req_tx_work),
GFP_KERNEL);
UT_ASSERT_PTR(NULL, !=, tx_work_ptr);
tx_work_ptr->handle = (void *)handle;
tx_work_ptr->pkt_priv = (void *)&cb_data;
tx_work_ptr->data = (void *)testdata;
tx_work_ptr->size = (size_t)strlen(testdata);
tx_work_ptr->req_intent = true;
tx_work_ptr->tx_stat.glink_tx_entered = false;
tx_work_ptr->tx_stat.glink_tx_exited = false;
init_completion(&tx_work_ptr->tx_worker_complete);
/* The riid passed below is hardcoded */
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_remote_rx_intent_put(
&mock_ptr->if_ptr, local_rcid, 0x10, (strlen(testdata) - 1));
/* Queue the glink_tx */
INIT_WORK(&tx_work_ptr->work, ut_tx_worker);
queue_work(ut_intent_req_workqueue, &tx_work_ptr->work);
UT_ASSERT_INT(
(int)wait_for_completion_timeout(&event, HZ / 2), >, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(RX_INTENT_REQ, ==, tx_cmd->type);
kfree(tx_cmd);
UT_ASSERT_BOOL(true , ==,
tx_work_ptr->tx_stat.glink_tx_entered);
UT_ASSERT_BOOL(false , ==,
tx_work_ptr->tx_stat.glink_tx_exited);
/* Generate Ack for rx_intent request from local channel */
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_rx_intent_req_ack
(&mock_ptr->if_ptr, local_rcid, true);
/* Pass an rx_intent of sufficient size */
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_remote_rx_intent_put(
&mock_ptr->if_ptr, local_rcid, 0x11, strlen(testdata));
UT_ASSERT_INT(
(int)wait_for_completion_timeout(&event, HZ / 2), >, 0);
tx_data = mock_xprt_get_tx_data(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_data);
UT_ASSERT_STRING_COMPARE(testdata, tx_data->data);
kfree(tx_data);
wait_for_completion(&tx_work_ptr->tx_worker_complete);
UT_ASSERT_BOOL(true , ==,
tx_work_ptr->tx_stat.glink_tx_entered);
UT_ASSERT_BOOL(true , ==,
tx_work_ptr->tx_stat.glink_tx_exited);
kfree(tx_work_ptr);
/* Close the G-Link Channel */
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_close.lcid);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, tx_cmd->local_close.lcid);
kfree(tx_cmd);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, 1);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
flush_workqueue(ut_intent_req_workqueue);
GLINK_STATUS(s, "\tOK\n");
} while (0);
unregister_completion(&event, mock_ptr);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
destroy_workqueue(ut_intent_req_workqueue);
}
/**
* do_mock_negotiation - helper function that does version negotiation
*
* @s: Pointer to output file
* @version: Version to negotiate
* @features: Feature to negotiate
*
* Return: 0 is negotiation successful, != 0 otherwise
*/
int do_mock_negotiation(struct seq_file *s, uint32_t version,
uint32_t features, int pr_index)
{
int failed = 0;
struct glink_mock_xprt *mock_ptr;
struct glink_mock_cmd *tx_cmd;
do {
mock_ptr = mock_xprt_get(pr_index);
/* Bring-up transport and do local negotiation */
mock_ptr->if_ptr.glink_core_if_ptr->link_up(&mock_ptr->if_ptr);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(VERSION, ==, tx_cmd->type);
UT_ASSERT_INT(0x5, ==, tx_cmd->version.version);
UT_ASSERT_INT(0x55, ==, tx_cmd->version.features);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version_ack(
&mock_ptr->if_ptr, version, features);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(VERSION, ==, tx_cmd->type);
UT_ASSERT_INT(version, ==, tx_cmd->version.version);
UT_ASSERT_INT(features, ==, tx_cmd->version.features);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version_ack(
&mock_ptr->if_ptr, version, features);
/* do remote negotiation */
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version(
&mock_ptr->if_ptr, version, features);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(VERSION_ACK, ==, tx_cmd->type);
kfree(tx_cmd);
/* confirm negotiation is now complete */
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(SET_VERSION, ==, tx_cmd->type);
UT_ASSERT_INT(version, ==, tx_cmd->version.version);
UT_ASSERT_INT(features, ==, tx_cmd->version.features);
kfree(tx_cmd);
} while (0);
return failed;
}
/**
* do_mock_negotiation_init - helper function that does version negotiation
* during init
*
* @version: Version to negotiate
* @features: Feature to negotiate
* @pr_index: Which mock transport priority to perform the negotiation on
*/
void do_mock_negotiation_init(uint32_t version, uint32_t features,
int pr_index)
{
struct glink_mock_xprt *mock_ptr;
struct glink_mock_cmd *tx_cmd;
int line = 0;
mock_ptr = mock_xprt_get(pr_index);
if (!mock_ptr) {
GLINK_UT_ERR("%s:%d mock_ptr is NULL\n", __func__, line);
return;
}
/*
* Bring-up transport and do local negotiation. Manually
* check for failures as the UT_ASSERT macros cannot be used
* here due to lack of a seq_file pointer.
*/
mock_ptr->if_ptr.glink_core_if_ptr->link_up(&mock_ptr->if_ptr);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
if (!tx_cmd) {
line = __LINE__;
goto fail;
}
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version_ack(
&mock_ptr->if_ptr, version, features);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
if (!tx_cmd) {
line = __LINE__;
goto fail;
}
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version_ack(
&mock_ptr->if_ptr, version, features);
/* do remote negotiation */
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version(
&mock_ptr->if_ptr, version, features);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
if (!tx_cmd) {
line = __LINE__;
goto fail;
}
kfree(tx_cmd);
/* confirm negotiation is now complete */
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
if (!tx_cmd) {
line = __LINE__;
goto fail;
}
if (SET_VERSION != tx_cmd->type ||
version != tx_cmd->version.version ||
features != tx_cmd->version.features)
GLINK_UT_ERR(
"%s: Version negotiation failed during init\n",
__func__);
kfree(tx_cmd);
return;
fail:
GLINK_UT_ERR("%s:%d tx_cmd is NULL\n", __func__, line);
}
/**
* glink_ut0_mock_basic_core - Core function for basic mock tests
* @s: pointer to output file
* @pr_index: Mock priority index
* @xprt_name: Mock transport name
*
* This test simulates open, read, write and close of a G-Link channel
* when the remote processor does not exist.
*/
static void glink_ut0_mock_basic_core(struct seq_file *s, unsigned pr_index,
char *xprt_name)
{
int failed = 0;
int ret;
void *handle = NULL;
struct glink_mock_xprt *mock_ptr;
struct glink_open_config open_cfg;
struct glink_mock_cmd *tx_cmd;
struct glink_mock_rx_intent *intent;
struct glink_mock_tx_data *tx_data;
struct glink_core_rx_intent *rx_intent_ptr;
struct ut_notify_data cb_data;
struct completion event;
do {
mock_ptr = mock_xprt_get(pr_index);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
init_completion(&event);
register_completion(&mock_ptr->if_ptr, &event);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0,
pr_index));
/* Open the channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = xprt_name;
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.priv = &cb_data;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* open channel completed */
/*
* Read the data [hello GLINK]
*/
/* 1. first sent intent for testdata length */
glink_queue_rx_intent(handle, (void *)&cb_data,
strlen(testdata));
intent = mock_xprt_get_intent(mock_ptr);
UT_ASSERT_PTR(NULL, !=, intent);
UT_ASSERT_INT(strlen(testdata), ==, intent->size);
/* 2. simulate remote write*/
rx_intent_ptr =
mock_ptr->if_ptr.glink_core_if_ptr->rx_get_pkt_ctx(
&mock_ptr->if_ptr, 1, intent->liid);
UT_ASSERT_PTR(NULL, ==, (void *)rx_intent_ptr->data);
rx_intent_ptr->data = (void *)testdata;
rx_intent_ptr->pkt_size = strlen(testdata);
mock_ptr->if_ptr.glink_core_if_ptr->rx_put_pkt_ctx(
&mock_ptr->if_ptr, 1, rx_intent_ptr, true);
/* 3. check glink_test_notify_rx() callback data*/
UT_ASSERT_INT(true, ==, cb_data.rx_notify);
UT_ASSERT_INT(strlen(testdata), ==, cb_data.size);
kfree(intent);
/*
* Write the data [Hello GLINK]
*/
/* 1. Simulate remote intent*/
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_remote_rx_intent_put(
&mock_ptr->if_ptr, 1, 1, strlen(testdata));
/* 2. write data */
ret = glink_tx(handle, (void *)&cb_data, (void *)testdata,
strlen(testdata), 0);
UT_ASSERT_INT(ret, ==, 0);
UT_ASSERT_INT(
(int)wait_for_completion_timeout(&event, HZ / 2), >, 0);
tx_data = mock_xprt_get_tx_data(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_data);
UT_ASSERT_STRING_COMPARE(testdata, tx_data->data);
/*
* 3. simulate and check tx_done and
* glink_test_notify_tx_done() callback data
*/
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_tx_done(
&mock_ptr->if_ptr, 1, 1, false);
UT_ASSERT_INT(true, == , cb_data.tx_done);
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_close.lcid);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, tx_cmd->local_close.lcid);
kfree(tx_cmd);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, 1);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
GLINK_STATUS(s, "\tOK\n");
} while (0);
unregister_completion(&event, mock_ptr);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
/**
* glink_ut0_mock_basic - Basic sanity test using local loopback.
*
* @s: pointer to output file
*
* This test simulates open, read, write and close of glink_channel
* when remote processor does not exits.
*/
static void glink_ut0_mock_basic(struct seq_file *s)
{
GLINK_STATUS(s, "Running %s\n", __func__);
glink_ut0_mock_basic_core(s, MOCK, "mock");
}
/**
* glink_ut0_mock_high_basic - Basic sanity test using local loopback.
*
* @s: pointer to output file
*
* This test simulates open, read, write and close of glink_channel
* when remote processor does not exits.
*/
static void glink_ut0_mock_high_basic(struct seq_file *s)
{
GLINK_STATUS(s, "Running %s\n", __func__);
glink_ut0_mock_basic_core(s, MOCK_HIGH, "mock_high");
}
/**
* glink_ut0_mock_low_basic - Basic sanity test using local loopback.
*
* @s: pointer to output file
*
* This test simulates open, read, write and close of glink_channel
* when remote processor does not exits.
*/
static void glink_ut0_mock_low_basic(struct seq_file *s)
{
GLINK_STATUS(s, "Running %s\n", __func__);
glink_ut0_mock_basic_core(s, MOCK_LOW, "mock_low");
}
/**
* glink_ut0_mock_ssr - Basic SSR test case using mock transport.
*
* @s: pointer to output file
*
* This test simulates a basic SSR case. A channel is opened, data
* is transmitted on it, and then SSR occurs. This test specifically tests
* the functionality of glink_ssr() in the G-Link core. It verifies that
* after an SSR, channels to the affected subsystem receive a
* REMOTE_DISCONNECTED notification, and verifies that the expected client
* response (calling glink_close()) is handled correctly.
*/
static void glink_ut0_mock_ssr(struct seq_file *s)
{
int failed = 0;
int ret;
void *handle = NULL;
struct glink_mock_xprt *mock_ptr;
struct glink_open_config open_cfg;
struct glink_mock_cmd *tx_cmd;
struct glink_mock_rx_intent *intent;
struct glink_mock_tx_data *tx_data;
struct glink_core_rx_intent *rx_intent_ptr;
struct ut_notify_data cb_data;
struct completion event;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
init_completion(&event);
register_completion(&mock_ptr->if_ptr, &event);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.priv = &cb_data;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
glink_queue_rx_intent(handle, (void *)&cb_data,
strlen(testdata));
intent = mock_xprt_get_intent(mock_ptr);
UT_ASSERT_PTR(NULL, !=, intent);
UT_ASSERT_INT(strlen(testdata), ==, intent->size);
rx_intent_ptr =
mock_ptr->if_ptr.glink_core_if_ptr->rx_get_pkt_ctx(
&mock_ptr->if_ptr, 1, intent->liid);
UT_ASSERT_PTR(NULL, ==, (void *)rx_intent_ptr->data);
rx_intent_ptr->data = (void *)testdata;
rx_intent_ptr->pkt_size = strlen(testdata);
mock_ptr->if_ptr.glink_core_if_ptr->rx_put_pkt_ctx(
&mock_ptr->if_ptr, 1, rx_intent_ptr, true);
UT_ASSERT_INT(true, ==, cb_data.rx_notify);
UT_ASSERT_INT(strlen(testdata), ==, cb_data.size);
kfree(intent);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_remote_rx_intent_put(
&mock_ptr->if_ptr, 1, 1, strlen(testdata));
ret = glink_tx(handle, (void *)&cb_data, (void *)testdata,
strlen(testdata), 0);
UT_ASSERT_INT(ret, ==, 0);
UT_ASSERT_INT(
(int)wait_for_completion_timeout(&event, HZ / 2), >, 0);
tx_data = mock_xprt_get_tx_data(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_data);
UT_ASSERT_STRING_COMPARE(testdata, tx_data->data);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_tx_done(
&mock_ptr->if_ptr, 1, 1, false);
UT_ASSERT_INT(true, == , cb_data.tx_done);
glink_ssr("local");
UT_ASSERT_INT(GLINK_REMOTE_DISCONNECTED, ==, cb_data.event);
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
glink_loopback_xprt_link_up();
GLINK_STATUS(s, "\tOK\n");
} while (0);
unregister_completion(&event, mock_ptr);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
/**
* glink_ut1_mock_open_close - Basic sanity test using local loopback.
*
* @s: pointer to output file
*
* This test simulates opening and closing a G-Link channel. When the close is
* completed, the local close ack is received before the remote close.
*/
static void glink_ut1_mock_open_close(struct seq_file *s)
{
int failed = 0;
int ret;
void *handle = NULL;
struct glink_mock_xprt *mock_ptr;
struct glink_open_config open_cfg;
struct glink_mock_cmd *tx_cmd;
struct ut_notify_data cb_data;
struct completion event;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
init_completion(&event);
register_completion(&mock_ptr->if_ptr, &event);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
/* Open the channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.priv = &cb_data;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* open channel completed */
/* close channel */
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_close.lcid);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, tx_cmd->local_close.lcid);
kfree(tx_cmd);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, 1);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
GLINK_STATUS(s, "\tOK\n");
} while (0);
unregister_completion(&event, mock_ptr);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
/**
* glink_ut1_mock_open_close_remote_first - Basic sanity test using local
* loopback.
*
* @s: pointer to output file
*
* This test simulates opening and closing a G-Link channel. In this test,
* the remote close is received before the local close ack.
*/
static void glink_ut1_mock_open_close_remote_first(struct seq_file *s)
{
int failed = 0;
int ret;
void *handle = NULL;
struct glink_mock_xprt *mock_ptr;
struct glink_open_config open_cfg;
struct glink_mock_cmd *tx_cmd;
struct ut_notify_data cb_data;
struct completion event;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
init_completion(&event);
register_completion(&mock_ptr->if_ptr, &event);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
/* Open the channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.priv = &cb_data;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* open channel completed */
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, 1);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_REMOTE_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* close channel */
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_close.lcid);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, 1);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
GLINK_STATUS(s, "\tOK\n");
} while (0);
unregister_completion(&event, mock_ptr);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
/**
* glink_ut1_mock_transmit_no_rx_intent - Transmit a packet on local loopback
* without first sending an rx intent.
*
* @s: pointer to output file
*
* This test simulates sending a packet when no rx intent has been queued.
*/
static void glink_ut1_mock_transmit_no_rx_intent(struct seq_file *s)
{
int failed = 0;
int ret;
void *handle = NULL;
struct glink_mock_xprt *mock_ptr;
struct glink_open_config open_cfg;
struct glink_mock_cmd *tx_cmd;
struct glink_mock_tx_data *tx_data;
struct ut_notify_data cb_data;
struct completion event;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
init_completion(&event);
register_completion(&mock_ptr->if_ptr, &event);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
/* Open the channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.priv = &cb_data;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* open channel completed */
/*
* Write the data [Hello GLINK]
* 1. Do the send, make sure it fails
* 2. Then do the intent, and try the send again; it should
* succeed
*/
/* write data without simulating receipt of an rx intent */
ret = glink_tx(handle, (void *)&cb_data, (void *)testdata,
strlen(testdata), 0);
/* glink_tx returns -EAGAIN if no rx_intent was present */
UT_ASSERT_INT(ret, ==, -EAGAIN);
/* 1. Simulate remote intent */
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_remote_rx_intent_put(
&mock_ptr->if_ptr, 1, 1, strlen(testdata));
/* 2. write data */
ret = glink_tx(handle, (void *)&cb_data, (void *)testdata,
strlen(testdata), 0);
/*
* 3. This time the write will succeed since an rx_intent was
* received.
*/
UT_ASSERT_INT(ret, ==, 0);
UT_ASSERT_INT(
(int)wait_for_completion_timeout(&event, HZ / 2), >, 0);
tx_data = mock_xprt_get_tx_data(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_data);
UT_ASSERT_STRING_COMPARE(testdata, tx_data->data);
/*
* 3. simulate and check tx_done and
* glink_test_notify_tx_done() callback data
*/
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_tx_done(
&mock_ptr->if_ptr, 1, 1, false);
UT_ASSERT_INT(true, == , cb_data.tx_done);
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_close.lcid);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, tx_cmd->local_close.lcid);
kfree(tx_cmd);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, 1);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
GLINK_STATUS(s, "\tOK\n");
} while (0);
unregister_completion(&event, mock_ptr);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
/**
* glink_ut1_mock_size_greater_than - Transmit a packet of size one byte
* greater than expected.
*
* @s: pointer to output file
*
* This test simulates open, write and close of glink_channel
* on the mock loopback transport. In particular, this test is for
* the case in which a packet is sent that has one more byte than the
* receiver expects.
*/
static void glink_ut1_mock_size_greater_than(struct seq_file *s)
{
int failed = 0;
int ret;
void *handle = NULL;
struct glink_mock_xprt *mock_ptr;
struct glink_open_config open_cfg;
struct glink_mock_cmd *tx_cmd;
struct ut_notify_data cb_data;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
/* Open the channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.priv = &cb_data;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* open channel completed */
/*
* Write the data [Hello GLINK]
*/
/* 1. Simulate remote intent for size one less than needed */
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_remote_rx_intent_put(
&mock_ptr->if_ptr, 1, 1, strlen(testdata - 1));
/*
* 2. write data - send one more byte than expected - glink_tx
* returns -EAGAIN if remote side has not provided a receive
* intent that is big enough
*/
ret = glink_tx(handle, (void *)&cb_data, (void *)testdata,
strlen(testdata), 0);
UT_ASSERT_INT(ret, ==, -EAGAIN);
/* Close channel */
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_close.lcid);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, tx_cmd->local_close.lcid);
kfree(tx_cmd);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, 1);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
/**
* glink_ut1_mock_read_no_rx_intent - Use mock transport to test
* failure case of attempting to read data
* without first queueing an rx intent.
*
* @s: pointer to output file
*
* This test simulates open, read, and close. First, a read is attempted
* without first queueing an rx_intent. This fails. Then a second read is
* attempted after queueing an rx_intent. This succeeds.
*/
static void glink_ut1_mock_read_no_rx_intent(struct seq_file *s)
{
int failed = 0;
int ret;
void *handle = NULL;
struct glink_mock_xprt *mock_ptr;
struct glink_open_config open_cfg;
struct glink_mock_cmd *tx_cmd;
struct glink_mock_rx_intent *intent;
struct glink_core_rx_intent *rx_intent_ptr;
struct ut_notify_data cb_data;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
cb_data_init(&cb_data);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
/* Open the channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = "loopback";
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.priv = &cb_data;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR(handle);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE("loopback", tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, 1, "loopback",
MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* open channel completed */
/*
* Read the data. Simulate remote attempting to write without
* queuing an rx intent and confirm that we cannot retrieve the
* RX intent.
*/
rx_intent_ptr =
mock_ptr->if_ptr.glink_core_if_ptr->rx_get_pkt_ctx(
&mock_ptr->if_ptr, 1, 0x1234);
UT_ASSERT_PTR(NULL, ==, rx_intent_ptr);
/* 3. check glink_test_notify_rx() callback data */
UT_ASSERT_INT(false, ==, cb_data.rx_notify);
/*
* Try again with rx intent queued and verify success
*/
/* 1. first, send intent for testdata length */
glink_queue_rx_intent(handle, (void *)&cb_data,
strlen(testdata));
intent = mock_xprt_get_intent(mock_ptr);
UT_ASSERT_PTR(NULL, !=, intent);
UT_ASSERT_INT(strlen(testdata), ==, intent->size);
/* 2. simulate remote write of more data than we asked for */
rx_intent_ptr =
mock_ptr->if_ptr.glink_core_if_ptr->rx_get_pkt_ctx(
&mock_ptr->if_ptr, 1, intent->liid);
UT_ASSERT_PTR(NULL, ==, (void *)rx_intent_ptr->data);
rx_intent_ptr->data = (void *)testdata;
rx_intent_ptr->pkt_size = strlen(testdata);
mock_ptr->if_ptr.glink_core_if_ptr->rx_put_pkt_ctx(
&mock_ptr->if_ptr, 1, rx_intent_ptr, true);
/* 3. check glink_test_notify_rx() callback data*/
UT_ASSERT_INT(true, ==, cb_data.rx_notify);
UT_ASSERT_INT(strlen(testdata), ==, cb_data.size);
kfree(intent);
/* Close channel */
ret = glink_close(handle);
UT_ASSERT_INT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->local_close.lcid);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, tx_cmd->local_close.lcid);
kfree(tx_cmd);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, 1);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(1, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
/**
* glink_ut0_mock_remote_negotiation - Use mock transport to test
* remote negotiation by sending
* different versio and features.
*
* @s: pointer to output file
*
* This test simulates linkup a transport and read the local supported
* version and feature and send different remote versions and fetures
* to validate the remote negotiation.
*/
static void glink_ut0_mock_remote_negotiation(struct seq_file *s)
{
int failed = 0;
struct glink_mock_xprt *mock_ptr;
struct glink_mock_cmd *tx_cmd;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
/* Bring-up transport and do local negotiation */
mock_ptr->if_ptr.glink_core_if_ptr->link_up(&mock_ptr->if_ptr);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(VERSION, ==, tx_cmd->type);
UT_ASSERT_INT(0x5, ==, tx_cmd->version.version);
UT_ASSERT_INT(0x55, ==, tx_cmd->version.features);
kfree(tx_cmd);
/*
* check with version which is not supported by local Glink
* core
*/
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version(
&mock_ptr->if_ptr,
0x6, 0x66);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(VERSION_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(0x6, >=, tx_cmd->version.version);
UT_ASSERT_INT(0x66, >=, tx_cmd->version.features);
kfree(tx_cmd);
mock_xprt_reset(mock_ptr);
/* Check with supported versioni but different features*/
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version(
&mock_ptr->if_ptr,
0x2, 0x33);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(VERSION_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(0x2, ==, tx_cmd->version.version);
UT_ASSERT_INT(0x22, ==, tx_cmd->version.features);
kfree(tx_cmd);
mock_xprt_reset(mock_ptr);
/* Check with supported version and features*/
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version(
&mock_ptr->if_ptr,
0x2, 0x22);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(VERSION_ACK, ==, tx_cmd->type);
UT_ASSERT_INT(0x2, ==, tx_cmd->version.version);
UT_ASSERT_INT(0x22, ==, tx_cmd->version.features);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version_ack(
&mock_ptr->if_ptr,
0x2, 0x22);
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(VERSION, ==, tx_cmd->type);
UT_ASSERT_INT(0x2, ==, tx_cmd->version.version);
UT_ASSERT_INT(0x22, ==, tx_cmd->version.features);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_version_ack(
&mock_ptr->if_ptr,
0x2, 0x22);
/* confirm negotiation is now complete */
tx_cmd = mock_xprt_get_next_cmd(mock_ptr);
UT_ASSERT_PTR(NULL, !=, tx_cmd);
UT_ASSERT_INT(SET_VERSION, ==, tx_cmd->type);
UT_ASSERT_INT(0x2, ==, tx_cmd->version.version);
UT_ASSERT_INT(0x22, ==, tx_cmd->version.features);
kfree(tx_cmd);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed)
GLINK_STATUS(s, "\tFailed\n");
}
/**
* glink_test_transport_teardown - Test transport teardown functionality
* @s: pointer to output file
*
* This function tests the transport teardown functionality. The test uses the
* glink_wait_link_down() API to tell when the remote subsystem has finished
* its link tear-down. Once this has occurred, the test completes the tear-down
* from the local side using glink_ssr(). This simulates the LK/RPM handoff use
* case, in which LK is communicating with RPM before Apps boots. For Apps to
* boot, the link between LK and RPM must be torn down first.
*/
static void glink_ft_transport_teardown(struct seq_file *s)
{
int failed = 0;
int ret = 0;
int sequence_number;
unsigned long timeout;
struct glink_dbgfs_data *dfs_d;
struct glink_ut_dbgfs *ut_dfs_d;
struct ut_notify_data cb_data;
struct subsys_info *ss_info;
struct do_cleanup_msg *do_cleanup = NULL;
void *handle = NULL;
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
cb_data_init(&cb_data);
ss_info = get_info_for_edge(ut_dfs_d->edge_name);
UT_ASSERT_PTR(ss_info, !=, NULL);
handle = ss_info->handle;
sequence_number = glink_ssr_get_seq_num();
do_cleanup = kzalloc(sizeof(*do_cleanup), GFP_KERNEL);
UT_ASSERT_PTR(do_cleanup, !=, NULL);
do_cleanup->version = 0;
do_cleanup->command = GLINK_SSR_DO_CLEANUP;
do_cleanup->seq_num = sequence_number;
do_cleanup->name_len = strlen("apss");
strlcpy(do_cleanup->name, "apss",
do_cleanup->name_len + 1);
ret = glink_tx(handle, (void *)&cb_data, (void *)do_cleanup,
sizeof(*do_cleanup), true);
UT_ASSERT_INT(ret, ==, 0);
timeout = jiffies + (1 * HZ);
while (!glink_wait_link_down(handle)) {
if (time_after(jiffies, timeout)) {
GLINK_UT_ERR("%s: %s\n", __func__,
"waiting more than 1 sec. for indices == 0");
failed = true;
break;
}
}
UT_ASSERT_BOOL(failed, !=, true);
glink_ssr(ss_info->edge);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (do_cleanup)
kfree(do_cleanup);
if (failed)
GLINK_STATUS(s, "\tFailed\n");
}
/**
* glink_ut0_ssr - SSR test
* @s: pointer to output file
*
* Return: Nothing
*
* This function triggers SSR on the subsystem given by ut_dfs_d->edge_name.
*/
static void glink_ut0_ssr(struct seq_file *s)
{
int failed = 0;
unsigned timeout_mult = 5;
struct glink_dbgfs_data *dfs_d;
struct glink_ut_dbgfs *ut_dfs_d;
struct subsys_info *ss_info;
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s for %s\n", __func__, ut_dfs_d->edge_name);
do {
ss_info = get_info_for_edge(ut_dfs_d->edge_name);
UT_ASSERT_PTR(ss_info, !=, NULL);
subsystem_restart(ss_info->ssr_name);
UT_ASSERT_BOOL(true, ==,
glink_ssr_wait_cleanup_done(timeout_mult));
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed)
GLINK_STATUS(s, "\tFailed\n");
}
/**
* glink_ft_ssr_bypass - SSR bypass test
* @s: pointer to output file
*
* Return: Nothing
*
* This function performs SSR from a G-Link perspective only. It performs
* local cleanup and sends the do_cleanup message to the remote subsystem,
* without an actual SSR occurring.
*/
static void glink_ft_ssr_bypass(struct seq_file *s)
{
struct glink_dbgfs_data *dfs_d;
struct glink_ut_dbgfs *ut_dfs_d;
struct subsys_info *ss_info;
int ret;
int failed = 0;
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s for %s\n", __func__, ut_dfs_d->edge_name);
do {
ss_info = get_info_for_edge(ut_dfs_d->edge_name);
UT_ASSERT_PTR(ss_info, !=, NULL);
glink_ssr(ss_info->edge);
ret = notify_for_subsystem(ss_info);
UT_ASSERT_INT(ret, ==, 0)
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed)
GLINK_STATUS(s, "\tFailed\n");
}
/**
* ut_loopback_stress_test - Stress test
*
* @s: pointer to output file
* @handle: handle returned by glink_open() for loopback control channel
* @data_handle: handle returned by glink_open() for loopback data channel
*
* This function used to stress test on the transport by doing
* multiple reads and writes with same data.
*/
static int ut_loopback_stress_test(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
struct req pkt;
int ret, i;
int len = strlen(testdata);
struct loopback_channel *lpb_ch =
(struct loopback_channel *)*data_handle;
GLINK_UT_INFO("%s: start for iterations[%d]\n", __func__,
ut_iterations);
pkt.payload.q_rx_int_conf.random_delay = 0;
pkt.payload.q_rx_int_conf.delay_ms = 0;
pkt.payload.q_rx_int_conf.num_intents = ut_iterations;
pkt.payload.q_rx_int_conf.intent_size = len;
strlcpy(pkt.payload.q_rx_int_conf.ch_name,
lpb_ch->open_cfg.name,
strlen(lpb_ch->open_cfg.name) + 1);
pkt.payload.q_rx_int_conf.name_len =
strlen(lpb_ch->open_cfg.name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
QUEUE_RX_INTENT_CONFIG, true);
if (ret) {
GLINK_UT_ERR("%s: glink_loopback_send_request failed\n",
__func__);
return ret;
}
for (i = 0; i < ut_iterations; i++) {
if (i == 0)
ret = glink_loopback_tx(*data_handle, (void *)testdata,
len, true, true, lpb_ch->rx_reuse);
else
ret = glink_loopback_tx(*data_handle, (void *)testdata,
len, false, false, lpb_ch->rx_reuse);
if (ret) {
GLINK_UT_ERR(
"%s:%s:%s %s: glink_loopback_tx failed\n",
lpb_ch->open_cfg.transport,
lpb_ch->open_cfg.edge,
lpb_ch->open_cfg.name, __func__);
return ret;
}
}
GLINK_UT_INFO("%s: END\n", __func__);
return 0;
}
/**
* ut_loopback_tp - Tracer Packet unit test
*
* @s: pointer to output file
* @handle: handle returned by glink_open() for loopback control channel
* @data_handle: handle returned by glink_open() for loopback data channel
*
* This function is used to send repeated tracer packets over the data channel.
*/
static int ut_loopback_tp(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
struct req pkt;
int ret, i;
int len = ut_tracer_pkt_size;
void *data;
struct loopback_channel *lpb_ch =
(struct loopback_channel *)*data_handle;
GLINK_UT_INFO("%s: start for iterations[%d]\n", __func__,
ut_iterations);
pkt.payload.q_rx_int_conf.random_delay = 0;
pkt.payload.q_rx_int_conf.delay_ms = 0;
pkt.payload.q_rx_int_conf.num_intents = ut_iterations;
pkt.payload.q_rx_int_conf.intent_size = len;
strlcpy(pkt.payload.q_rx_int_conf.ch_name,
lpb_ch->open_cfg.name,
strlen(lpb_ch->open_cfg.name) + 1);
pkt.payload.q_rx_int_conf.name_len =
strlen(lpb_ch->open_cfg.name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
QUEUE_RX_INTENT_CONFIG, true);
if (ret) {
GLINK_UT_ERR("%s: glink_loopback_send_request failed\n",
__func__);
return ret;
}
data = kzalloc(len, GFP_KERNEL);
if (!data) {
GLINK_UT_ERR("%s: Tracer Packet Allocation failed\n",
__func__);
return -ENOMEM;
}
for (i = 0; i < ut_iterations; i++) {
tracer_pkt_init(data, (size_t)len, 0xFFFF, 0xFFFFFFFF,
&i, sizeof(i));
if (i == 0)
ret = glink_loopback_tx_tp(*data_handle,
(void *)data,
len, true, true, lpb_ch->rx_reuse);
else
ret = glink_loopback_tx_tp(*data_handle,
(void *)data,
len, false, false, lpb_ch->rx_reuse);
if (ret) {
GLINK_UT_ERR(
"%s:%s:%s %s: glink_loopback_tx failed\n",
lpb_ch->open_cfg.transport,
lpb_ch->open_cfg.edge,
lpb_ch->open_cfg.name, __func__);
return ret;
}
glink_loopback_hex_dump_tp(s, data, len);
}
GLINK_UT_INFO("%s: END\n", __func__);
return 0;
}
/**
* ut_loopback_performance_test - Perfromance test
*
* @s: pointer to output file
* @handle: handle returned by glink_open() for loopback control channel
* @data_handle: handle returned by glink_open() for loopback data channel
*
* This function used to do performance test on the transport by
* doing multiple reads and writes with different sizes of data.
*/
static int ut_loopback_performance_test(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
uint32_t size;
char *data = NULL;
struct req pkt;
int ret, i;
uint64_t t_start, t_end;
uint64_t rtt_us, throughput_kBps;
unsigned long rem;
struct loopback_channel *lpb_ch =
(struct loopback_channel *)*data_handle;
int len = strlen(lpb_ch->open_cfg.name);
int ut_iterations_temp = ut_iterations * 10;
if (ut_iterations_temp > MAX_PERF_ITERATIONS)
ut_iterations_temp = MAX_PERF_ITERATIONS;
GLINK_UT_INFO("%s: start for iterations[%d]\n", __func__,
ut_iterations_temp);
GLINK_STATUS(s, "Size\tIterations\tRTT(us)\tThroughput(KB/s)\n");
for (size = 1; size <= SZ_8K; size = size * 2) {
pkt.payload.q_rx_int_conf.random_delay = 0;
pkt.payload.q_rx_int_conf.delay_ms = 0;
pkt.payload.q_rx_int_conf.num_intents = ut_iterations_temp;
pkt.payload.q_rx_int_conf.intent_size = size;
strlcpy(pkt.payload.q_rx_int_conf.ch_name,
lpb_ch->open_cfg.name, len + 1);
pkt.payload.q_rx_int_conf.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt,
QUEUE_RX_INTENT_CONFIG, true);
if (ret) {
GLINK_UT_ERR("%s: glink_loopback_send_request failed\n",
__func__);
return ret;
}
data = kzalloc(size, GFP_KERNEL);
if (!data) {
GLINK_UT_ERR("%s: No memory for allocation\n",
__func__);
return -ENOMEM;
}
memset(data, size, size);
t_start = sched_clock();
for (i = 0; i < ut_iterations_temp; i++) {
if (i == 0)
ret = glink_loopback_tx(*data_handle, data,
size, true, true , false);
else
ret = glink_loopback_tx(*data_handle, data,
size, false, true, false);
if (ret) {
GLINK_UT_ERR(
"%s:%s:%s %s: glink_loopback_tx failed\n",
lpb_ch->open_cfg.transport,
lpb_ch->open_cfg.edge,
lpb_ch->open_cfg.name, __func__);
return ret;
}
}
t_end = sched_clock();
rtt_us = t_end - t_start;
rem = do_div(rtt_us, (1000 * ut_iterations_temp));
/*
* Convert B / us to KB/s
*
* B 10^6 us 1 KB 10^6 KB 15625 KB
* -- * ------- * ------- = ------- = --------
* us 1 s 1024 B 1024 s 16 S
*/
throughput_kBps = (15625 * size * 2);
rem = do_div(throughput_kBps, (16 * rtt_us));
GLINK_STATUS(s, "%u\t%u\t\t%u\t%u\n", size, ut_iterations_temp,
(unsigned int)rtt_us,
(unsigned int)throughput_kBps);
kfree(data);
}
GLINK_UT_INFO("%s: END\n", __func__);
return 0;
}
/**
* ut_loopback_open_close_test - Re-open test
*
* @s: pointer to output file
* @handle: handle returned by glink_open() for loopback control channel
* @data_handle: handle returned by glink_open() for loopback data channel
*
* This function used to do open and close test on the transport by
* doing multiple times open and close of same channel.
*/
static int ut_loopback_open_close_test(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
int i;
int ret;
struct loopback_channel *lpb_ch =
(struct loopback_channel *)*data_handle;
const char *transport = lpb_ch->open_cfg.transport;
const char *edge = lpb_ch->open_cfg.edge;
const char *name = lpb_ch->open_cfg.name;
int len = strlen(name);
struct req pkt;
GLINK_UT_INFO("%s: start for iterations[%d]\n", __func__,
ut_iterations);
for (i = 0; i < ut_iterations; i++) {
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name, len + 1);
pkt.payload.close.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
if (ret) {
GLINK_UT_INFO("%s: remote close fail ret[%d]\n",
__func__, ret);
return ret;
}
ret = glink_loopback_close(*data_handle);
*data_handle = NULL;
if (ret) {
GLINK_UT_INFO("%s:%s:%s %s: close fail ret[%d]\n",
transport,
lpb_ch->open_cfg.edge,
lpb_ch->open_cfg.name,
__func__, ret);
return ret;
}
pkt.payload.open.delay_ms = 0;
strlcpy(pkt.payload.open.ch_name, name, len + 1);
pkt.payload.open.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt, OPEN,
true);
if (ret) {
GLINK_UT_INFO("%s: remote close fail ret[%d]\n",
__func__, ret);
return ret;
}
*data_handle = glink_loopback_open(transport, edge, name,
CH_DATA_TYPE, LINEAR_RX, false);
if (!(*data_handle)) {
GLINK_UT_INFO("%s: open fail ret[%d]\n", __func__, ret);
return -ENODEV;
}
}
GLINK_UT_INFO("%s: END\n", __func__);
return 0;
}
/**
* ut_loopback_queue_rx_intent_test - Queue rx intent test
*
* @s: pointer to output file
* @handle: handle returned by glink_open() for loopback control channel
* @data_handle: handle returned by glink_open() for loopback data channel
*
* This function used to queue muliple rx intent on data channel and
* repeat same on every re-open.
*/
static int ut_loopback_queue_rx_intent_test(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
int i;
uint32_t size;
int ret;
struct loopback_channel *lpb_ch =
(struct loopback_channel *)*data_handle;
const char *transport = lpb_ch->open_cfg.transport;
const char *edge = lpb_ch->open_cfg.edge;
const char *name = lpb_ch->open_cfg.name;
int len = strlen(name);
struct req pkt;
GLINK_UT_INFO("%s: start for iterations[%d]\n", __func__,
ut_iterations);
for (i = 0; i < ut_iterations; i++) {
lpb_ch = (struct loopback_channel *)*data_handle;
for (size = 1; size <= SZ_8K; size = size * 2) {
ret = glink_queue_rx_intent(lpb_ch->handle,
(void *)lpb_ch, size);
if (ret) {
GLINK_UT_ERR("%s: %s ret[%d]\n", __func__,
"glink_queue_rx_intent failed",
ret);
return ret;
}
}
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name, len + 1);
pkt.payload.close.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
if (ret) {
GLINK_UT_INFO("%s: remote close fail ret[%d]\n",
__func__, ret);
return ret;
}
ret = glink_loopback_close(*data_handle);
*data_handle = NULL;
if (ret) {
GLINK_UT_INFO("%s:%s:%s %s: close fail ret[%d]\n",
transport, edge, name, __func__, ret);
return ret;
}
pkt.payload.open.delay_ms = 0;
strlcpy(pkt.payload.open.ch_name, name, len + 1);
pkt.payload.open.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt, OPEN,
true);
if (ret) {
GLINK_UT_INFO("%s: remote close fail ret[%d]\n",
__func__, ret);
return ret;
}
*data_handle = glink_loopback_open(transport, edge, name,
CH_DATA_TYPE, LINEAR_RX, false);
if (!(*data_handle)) {
GLINK_UT_INFO("%s:%s:%s %s: open fail ret[%d]\n",
transport, edge, name, __func__, ret);
return -ENODEV;
}
}
GLINK_UT_INFO("%s: END\n", __func__);
return 0;
}
/**
* ut_loopback_tx_with_req_intent_test - Tx with req_intent test
*
* @s: pointer to output file
* @handle: handle returned by glink_open() for loopback control channel
* @data_handle: handle returned by glink_open() for loopback data channel
*
* This function used to transmit data with with no intent queue by remote
* client.
*/
static int ut_loopback_tx_with_req_intent_test(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
uint32_t size;
char *data = NULL;
int ret;
GLINK_UT_INFO("%s: start for iterations[%d]\n", __func__,
ut_iterations);
for (size = 1; size <= SZ_8K; size = size * 2) {
data = kzalloc(size, GFP_KERNEL);
if (!data) {
GLINK_UT_ERR("%s: No memory for allocation\n",
__func__);
return -ENOMEM;
}
memset(data, size, size);
ret = glink_loopback_tx(*data_handle, data, size, true, true,
false);
if (ret) {
GLINK_UT_ERR("%s: glink_loopback_tx failed\n",
__func__);
return ret;
}
kfree(data);
}
GLINK_UT_INFO("%s: END\n", __func__);
return 0;
}
static void *ut_skb_vbuf_provider(void *iovec, size_t offset, size_t *buf_size)
{
struct sk_buff *skb;
struct sk_buff_head *skb_head = (struct sk_buff_head *)iovec;
size_t temp_size = 0;
*buf_size = 0;
skb_queue_walk(skb_head, skb) {
temp_size += skb->len;
if (offset >= temp_size)
continue;
*buf_size = temp_size - offset;
return (void *)skb->data + skb->len - *buf_size;
}
return NULL;
}
static void free_skb_vector(struct sk_buff_head *skb_head)
{
struct sk_buff *skb;
while (!skb_queue_empty(skb_head)) {
skb = skb_dequeue(skb_head);
kfree_skb(skb);
}
kfree(skb_head);
}
static struct sk_buff_head *create_skb_vector(int buf_count,
int buf_len, char *data)
{
struct sk_buff_head *skb_head;
struct sk_buff *skb;
void *dest_data;
int i;
skb_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
if (!skb_head) {
GLINK_UT_ERR("%s: Failed to allocate skb_head\n", __func__);
return NULL;
}
skb_queue_head_init(skb_head);
for (i = 0; i < buf_count; i++) {
skb = alloc_skb(buf_len, GFP_KERNEL);
if (!skb) {
GLINK_UT_ERR("%s: Error allocating SKB@%d iteration\n",
__func__, i);
goto out_create_skb_vector;
}
dest_data = skb_put(skb, buf_len);
memcpy(skb->data, data, buf_len);
skb_queue_tail(skb_head, skb);
}
return skb_head;
out_create_skb_vector:
free_skb_vector(skb_head);
return NULL;
}
/**
* ut_loopback_skb_stress_test - SKB Stress test
*
* @s: pointer to output file
* @handle: handle returned by glink_open() for loopback control channel
* @data_handle: handle returned by glink_open() for loopback data channel
*
* This function used to perform vectored stress test on the transport by doing
* multiple SKB reads and writes with same data.
*/
static int ut_loopback_skb_stress_test(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
struct req pkt;
int ret, i;
int len = strlen(testdata);
int buf_count = ut_vector_buf_count;
struct loopback_channel *lpb_ch =
(struct loopback_channel *)*data_handle;
struct sk_buff_head *skb_head;
GLINK_UT_INFO("%s: start for iterations[%d]\n", __func__,
ut_iterations);
skb_head = create_skb_vector(buf_count, len, (char *)testdata);
if (!skb_head) {
GLINK_UT_INFO("%s: Error creating skb vector\n", __func__);
return -ENOMEM;
}
pkt.payload.q_rx_int_conf.random_delay = 0;
pkt.payload.q_rx_int_conf.delay_ms = 0;
pkt.payload.q_rx_int_conf.num_intents = ut_iterations;
pkt.payload.q_rx_int_conf.intent_size = buf_count * len;
strlcpy(pkt.payload.q_rx_int_conf.ch_name,
lpb_ch->open_cfg.name,
strlen(lpb_ch->open_cfg.name) + 1);
pkt.payload.q_rx_int_conf.name_len =
strlen(lpb_ch->open_cfg.name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
QUEUE_RX_INTENT_CONFIG, true);
if (ret) {
GLINK_UT_ERR("%s: glink_loopback_send_request failed\n",
__func__);
free_skb_vector(skb_head);
return ret;
}
for (i = 0; i < ut_iterations; i++) {
ret = glink_loopback_txv(*data_handle, (void *)skb_head,
buf_count * len, ut_skb_vbuf_provider,
NULL, false);
if (ret) {
GLINK_UT_ERR(
"%s:%s:%s %s: glink_loopback_tx failed\n",
lpb_ch->open_cfg.transport,
lpb_ch->open_cfg.edge,
lpb_ch->open_cfg.name, __func__);
return ret;
}
}
GLINK_UT_INFO("%s: END\n", __func__);
return 0;
}
/**
* ut_loopback_sigs - Signal test
*
* @s: pointer to output file
* @handle: handle returned by glink_open() for loopback control channel
* @data_handle: handle returned by glink_open() for loopback data channel
*
* This function used to test signals of the channel by
* set signal and remote get signal.
*/
static int ut_loopback_sigs(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
int ret;
uint32_t i;
GLINK_UT_INFO("%s: start for iterations[%d]\n", __func__,
ut_iterations);
for (i = 0; i < ut_iterations; i++) {
ret = glink_loopback_sigs_set(*data_handle, i);
if (ret) {
GLINK_UT_ERR("%s: glink_loopback_sigs_set failed\n",
__func__);
return ret;
}
}
GLINK_UT_INFO("%s: END\n", __func__);
return 0;
}
/**
* glink_ut_local_basic_core - The loopback core function.
*
* @s: pointer to output file
* @xprt_id:
* @name: test name
* @test_type: Type of the test to run
*
* This test simulates a simple write and read
* when remote processor does not exist.
*/
static void glink_ut_local_basic_core(struct seq_file *s)
{
struct glink_dbgfs_data *dfs_d;
struct glink_ut_dbgfs *ut_dfs_d;
int failed = 0;
int ret;
void *cntl_handle = NULL;
void *data_handle = NULL;
char *cntl_ch_name;
char *data_ch_name;
bool b_ch_mem_alloc = false;
unsigned int cntl_ch_len;
unsigned int data_ch_len;
char edge[GLINK_NAME_SIZE] = {0};
const char *cntl_ch_pfix = "LOOPBACK_CTL_";
const char *data_ch_pfix = "LOOPBACK_DATA_";
int len = 0;
struct req pkt;
int rx_type;
testfunc do_test;
bool rx_reuse = false;
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
do_test = ut_dfs_d->tfunc;
if (!strcmp(ut_dfs_d->ut_name, "ut0_skb_basic")
|| !strcmp(ut_dfs_d->ut_name, "ut0_skb_stress"))
rx_type = LINEAR_VECTOR_RX;
else
rx_type = LINEAR_RX;
if (!strcmp(ut_dfs_d->ut_name, "ut1_rx_reuse")
|| !strcmp(ut_dfs_d->ut_name, "ut1_smem_rx_reuse"))
rx_reuse = true;
GLINK_STATUS(s, "Running %s\n", ut_dfs_d->ut_name);
do {
if (!strcmp(ut_dfs_d->xprt_name, "lloop")) {
data_ch_name = "LOCAL_DATA_LOOP_CLNT";
cntl_handle = glink_loopback_open("lloop",
"local",
"LOCAL_LOOPBACK_CLNT",
CH_CNTL_TYPE, LINEAR_RX, false);
} else {
glink_ut_to_upper(edge, ut_dfs_d->edge_name);
cntl_ch_len = GLINK_NAME_SIZE + strlen(cntl_ch_pfix);
cntl_ch_name = kzalloc(cntl_ch_len,
GFP_KERNEL);
UT_ASSERT_ERR_PTR(cntl_ch_name);
snprintf(cntl_ch_name, cntl_ch_len,
"%s%s", cntl_ch_pfix, edge);
data_ch_len = GLINK_NAME_SIZE + strlen(data_ch_pfix);
data_ch_name = kzalloc(data_ch_len,
GFP_KERNEL);
UT_ASSERT_ERR_PTR(data_ch_name);
snprintf(data_ch_name, data_ch_len,
"%s%s", data_ch_pfix, edge);
b_ch_mem_alloc = true;
cntl_handle = glink_loopback_open(ut_dfs_d->xprt_name,
ut_dfs_d->edge_name,
cntl_ch_name,
CH_CNTL_TYPE,
LINEAR_RX, false);
}
UT_ASSERT_ERR_PTR(cntl_handle);
len = strlen(data_ch_name);
pkt.payload.open.delay_ms = 0;
strlcpy(pkt.payload.open.ch_name, data_ch_name, len + 1);
pkt.payload.open.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt, OPEN,
true);
UT_ASSERT_INT(ret, ==, 0);
pkt.payload.tx_conf.random_delay = 0;
pkt.payload.tx_conf.delay_ms = 0;
pkt.payload.tx_conf.echo_count = 1;
pkt.payload.tx_conf.transform_type = NO_TRANSFORM;
strlcpy(pkt.payload.tx_conf.ch_name, data_ch_name, len + 1);
pkt.payload.tx_conf.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt, TX_CONFIG,
true);
UT_ASSERT_INT(ret, ==, 0);
pkt.payload.rx_done_conf.random_delay = 0;
pkt.payload.rx_done_conf.delay_ms = 0;
strlcpy(pkt.payload.rx_done_conf.ch_name,
data_ch_name, len + 1);
pkt.payload.rx_done_conf.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt,
RX_DONE_CONFIG, true);
UT_ASSERT_INT(ret, ==, 0);
/* data channel */
if (!strcmp(ut_dfs_d->xprt_name, "lloop"))
data_handle = glink_loopback_open("lloop",
"local",
"LOCAL_DATA_LOOP_CLNT",
CH_DATA_TYPE, rx_type,
rx_reuse);
else
data_handle = glink_loopback_open(ut_dfs_d->xprt_name,
ut_dfs_d->edge_name,
data_ch_name,
CH_DATA_TYPE, rx_type,
rx_reuse);
UT_ASSERT_ERR_PTR(data_handle);
ret = do_test(s, cntl_handle, &data_handle);
UT_ASSERT_INT(ret, ==, 0);
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, data_ch_name, len + 1);
pkt.payload.close.name_len = len;
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT(ret, ==, 0);
ret = glink_loopback_close(data_handle);
UT_ASSERT_INT(ret, ==, 0);
data_handle = NULL;
/* Close the control Channel */
ret = glink_loopback_close(cntl_handle);
UT_ASSERT_INT(ret, ==, 0);
cntl_handle = NULL;
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed) {
GLINK_STATUS(s, "\tFailed\n");
glink_loopback_close(data_handle);
glink_loopback_close(cntl_handle);
}
if (b_ch_mem_alloc) {
kfree(cntl_ch_name);
kfree(data_ch_name);
}
}
/**
* glink_ut_mt_open_ctl_ch() - Construct the control channel name & open it
* @edge: Edge on which the control channel has to be opened.
* @xprt: Transport on which the control channel has to be opened.
*
* @return: Return the value as returned by glink_open()
*
* This function is used by multi-threaded tests to construct a control
* channel name depending on the edge and then open that channel.
*/
static void *glink_ut_mt_open_ctl_ch(char *edge, char *xprt)
{
char ch_name[GLINK_NAME_SIZE];
char ch_name_suffix[GLINK_NAME_SIZE];
if (!strcmp(xprt, "lloop"))
return glink_loopback_open(xprt, "local",
"LOCAL_LOOPBACK_CLNT", CH_CNTL_TYPE, LINEAR_RX,
false);
strlcpy(ch_name, "LOOPBACK_CTL_", GLINK_NAME_SIZE);
glink_ut_to_upper(ch_name_suffix, edge);
ch_name_suffix[strlen(edge)] = '\0';
strlcat(ch_name, ch_name_suffix, GLINK_NAME_SIZE);
return glink_loopback_open(xprt, edge, ch_name, CH_CNTL_TYPE,
LINEAR_RX, false);
}
/**
* glink_ut_mt_send_config_reqs() - Send Configuration requests for data
* channels by a multi-threaded test
* @s: Pointer to output file.
* @cntl_handle: Handle to the control channel.
* @mt_thread_info: Configuration info specific to the thread/client.
*
* @return: 0 on success, 1 on failure.
*
* This function is used by the multi-threaded test to send configuration
* requests for data channel to be used by a client/thread of the multi-
* threaded test.
*/
static int glink_ut_mt_send_config_reqs(struct seq_file *s,
void *cntl_handle, struct mt_test_info_struct *mt_thread_info)
{
struct req pkt;
int ret;
int failed = 0;
struct loopback_channel *lpb_ch =
(struct loopback_channel *)cntl_handle;
struct ch_config *config = &mt_thread_info->ch_cfg;
const char *cntl_transport = lpb_ch->open_cfg.transport;
const char *cntl_edge = lpb_ch->open_cfg.edge;
const char *cntl_name = lpb_ch->open_cfg.name;
void *data_handle = NULL;
do {
pkt.payload.open.delay_ms = 0;
strlcpy(pkt.payload.open.ch_name, config->ch_name,
GLINK_NAME_SIZE);
pkt.payload.open.name_len = strlen(config->ch_name);
ret = glink_loopback_send_request(cntl_handle, &pkt, OPEN,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
pkt.payload.tx_conf.random_delay = 0;
pkt.payload.tx_conf.delay_ms = config->tx_delay_ms;
pkt.payload.tx_conf.echo_count = 1;
pkt.payload.tx_conf.transform_type = NO_TRANSFORM;
strlcpy(pkt.payload.tx_conf.ch_name, config->ch_name,
GLINK_NAME_SIZE);
pkt.payload.tx_conf.name_len = strlen(config->ch_name);
ret = glink_loopback_send_request(cntl_handle, &pkt, TX_CONFIG,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s:%s:%s %s: %s %s, thread: %d\n",
cntl_transport, cntl_edge, cntl_name, __func__,
"Sent TX_CONFIG request for data channel",
config->ch_name, current->pid);
pkt.payload.rx_done_conf.random_delay = 0;
pkt.payload.rx_done_conf.delay_ms = 0;
strlcpy(pkt.payload.rx_done_conf.ch_name, config->ch_name,
GLINK_NAME_SIZE);
pkt.payload.rx_done_conf.name_len = strlen(config->ch_name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
RX_DONE_CONFIG, true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s:%s:%s %s: %s %s, thread: %d\n",
cntl_transport, cntl_edge, cntl_name, __func__,
"Sent RX_DONE_CONFIG request for data channel",
config->ch_name, current->pid);
/*
* Open data channel here, before sending the
* QUEUE_RX_INTENT_CONFIG request.
*/
data_handle = glink_loopback_open(cntl_transport, cntl_edge,
config->ch_name, CH_DATA_TYPE,
LINEAR_RX_NOWAIT, false);
UT_ASSERT_ERR_PTR_MT(data_handle);
mt_thread_info->data_handle = data_handle;
GLINK_UT_DBG("%s: start for iterations[%d]\n", __func__,
config->num_pkts);
pkt.payload.q_rx_int_conf.random_delay = 0;
pkt.payload.q_rx_int_conf.delay_ms = 0;
pkt.payload.q_rx_int_conf.num_intents = config->num_pkts;
pkt.payload.q_rx_int_conf.intent_size = config->data_len;
strlcpy(pkt.payload.q_rx_int_conf.ch_name, config->ch_name,
GLINK_NAME_SIZE);
pkt.payload.q_rx_int_conf.name_len = strlen(config->ch_name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
QUEUE_RX_INTENT_CONFIG, true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s:%s:%s %s: %s %s, thread: %d\n",
cntl_transport, cntl_edge, cntl_name, __func__,
"Sent QUEUE_RX_INTENT_CONFIG request for data channel",
config->ch_name, current->pid);
} while (0);
if (failed) {
glink_loopback_close(data_handle);
ret = 1;
}
return ret;
}
/**
* init_mt_cb_data() - Initialize callback data for multi-threaded tests
* @cb_data: Pointer to the multi-thread callback data.
*/
static void init_mt_cb_data(struct mt_cb_data *cb_data)
{
init_waitqueue_head(&cb_data->test_comp);
atomic_set(&cb_data->num_tx_pkts, 0);
atomic_set(&cb_data->num_rx_pkts, 0);
}
#define glink_ut_mt_wait_cb_data(cb_data) \
wait_event_interruptible((cb_data)->test_comp, \
atomic_read(&((cb_data)->num_tx_pkts)) == \
atomic_read(&((cb_data)->num_rx_pkts)))
/**
* construct_ch_name() - Construct data channel name for multi-threaded tests
* @dest: Destination Buffer where the channel name will be put.
* @prefix: Prefix of the channel name.
* @index: Index of the channel name.
*/
static void construct_ch_name(char *dest, char *prefix, int index)
{
char *suffix = "_CLNT";
scnprintf(dest, GLINK_NAME_SIZE, "%s%d%s", prefix, index, suffix);
}
/**
* ut_mt_worker_func() - Worker function for multi-threaded test
* @work: Work structure containing information about the work to be done.
*/
static void ut_mt_worker_func(struct work_struct *work)
{
int failed = 0;
int ret;
int i;
void *data_handle = NULL;
struct seq_file *s;
struct mt_test_info_struct *mt_thread_info;
struct loopback_channel *lpb_ch;
const char *transport;
const char *edge;
const char *name;
struct ch_config *config;
bool req_intent = true;
void *data = NULL;
struct mt_cb_data cb_data;
struct req pkt;
mt_thread_info = container_of(work,
struct mt_test_info_struct, work);
config = &mt_thread_info->ch_cfg;
data_handle = mt_thread_info->data_handle;
s = mt_thread_info->s;
name = mt_thread_info->name;
lpb_ch = (struct loopback_channel *)mt_thread_info->cntl_handle;
transport = lpb_ch->open_cfg.transport;
edge = lpb_ch->open_cfg.edge;
init_mt_cb_data(&cb_data);
do {
data = kmalloc(config->data_len, GFP_KERNEL);
UT_ASSERT_PTR_MT(data, !=, NULL);
memset(data, mt_thread_info->index, config->data_len);
for (i = 0; i < config->num_pkts; i++) {
ret = glink_loopback_tx_nowait(data_handle,
data, config->data_len, req_intent,
&cb_data);
UT_ASSERT_INT_MT(ret, ==, 0);
if (failed)
break;
GLINK_UT_DBG("%s:%s:%s %s: %s iteration: %d, tid:%d\n",
transport, edge, name, __func__,
"Called glink_loopback_tx(), ",
i, current->pid);
if (config->pkt_ntrvl)
msleep(config->pkt_ntrvl);
req_intent = false;
}
if (failed)
break;
glink_ut_mt_wait_cb_data(&cb_data);
} while (0);
kfree(data);
if (failed)
*mt_thread_info->result = 1;
/* Close the data handle, but not the control handle */
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name, GLINK_NAME_SIZE);
pkt.payload.close.name_len = strlen(name);
ret = glink_loopback_send_request((void *)lpb_ch, &pkt, CLOSE, true);
ret = glink_loopback_close(data_handle);
/* Wakeup the main thread */
GLINK_UT_DBG("%s: Before dec, mt_test_num_workers = %d. Tid: %d\n",
__func__, atomic_read(mt_thread_info->num_workers),
current->pid);
atomic_dec(mt_thread_info->num_workers);
GLINK_UT_DBG("%s: After dec, mt_test_num_workers = %d. Tid: %d\n",
__func__, atomic_read(mt_thread_info->num_workers),
current->pid);
wake_up(mt_thread_info->waitqueue);
}
/**
* glink_ut_mt_test_core() - Multi-threaded test core
* @s: Output file to write to.
*/
static void glink_ut_mt_test_core(struct seq_file *s)
{
struct glink_dbgfs_data *dfs_d;
struct glink_ut_dbgfs *ut_dfs_d;
testfunc do_test;
void *cntl_handle;
atomic_t mt_test_num_workers = ATOMIC_INIT(0);
wait_queue_head_t waitqueue;
int i = 0, j;
int ret;
int failed = 0;
int result = 0;
struct req pkt;
uint32_t pkt_ntrvl = (uint32_t)ut_mt_lat_pkt_ntrvl_ms;
uint32_t num_lat_clnts = (uint32_t)ut_mt_num_lat_clnts;
uint32_t num_tput_clnts = (uint32_t)ut_mt_num_tput_clnts;
uint32_t num_pkts_lat_ch = (uint32_t)ut_mt_num_pkts_lat_ch;
uint32_t num_pkts_tput_ch = (uint32_t)ut_mt_num_pkts_tput_ch;
size_t pkt_size_lat_ch = (uint32_t)ut_mt_pkt_size_lat_ch;
size_t pkt_size_tput_ch = (uint32_t)ut_mt_pkt_size_tput_ch;
struct workqueue_struct **mt_test_wq = NULL;
struct mt_test_info_struct *mt_thread_info = NULL;
GLINK_STATUS_MT(s, "Running %s\n", __func__);
GLINK_INFO_PERF("%s config: %u, %u, %u, %u, %u, %zu, %zu\n", __func__,
pkt_ntrvl, num_lat_clnts, num_tput_clnts, num_pkts_lat_ch,
num_pkts_tput_ch, pkt_size_lat_ch, pkt_size_tput_ch);
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
do_test = ut_dfs_d->tfunc;
init_waitqueue_head(&waitqueue);
do {
cntl_handle = glink_ut_mt_open_ctl_ch(ut_dfs_d->edge_name,
ut_dfs_d->xprt_name);
UT_ASSERT_ERR_PTR_MT(cntl_handle);
atomic_set(&mt_test_num_workers,
(num_lat_clnts + num_tput_clnts));
mt_thread_info = kzalloc(sizeof(*mt_thread_info) *
(num_lat_clnts + num_tput_clnts),
GFP_KERNEL);
UT_ASSERT_ERR_PTR_MT(mt_thread_info);
for (i = 0; i < num_lat_clnts; i++) {
mt_thread_info[i].s = s;
mt_thread_info[i].cntl_handle = cntl_handle;
mt_thread_info[i].num_workers = &mt_test_num_workers;
mt_thread_info[i].waitqueue = &waitqueue;
mt_thread_info[i].result = &result;
mt_thread_info[i].index = i;
mt_thread_info[i].ch_cfg.pkt_ntrvl = pkt_ntrvl;
mt_thread_info[i].ch_cfg.num_pkts = num_pkts_lat_ch;
mt_thread_info[i].ch_cfg.tx_delay_ms = 500;
mt_thread_info[i].ch_cfg.data_len = pkt_size_lat_ch;
construct_ch_name(mt_thread_info[i].name, "LL_LAT_", i);
INIT_WORK(&mt_thread_info[i].work,
ut_mt_worker_func);
strlcpy(mt_thread_info[i].ch_cfg.ch_name,
mt_thread_info[i].name,
GLINK_NAME_SIZE);
ret = glink_ut_mt_send_config_reqs(s, cntl_handle,
&mt_thread_info[i]);
UT_ASSERT_INT_MT(ret, ==, 0);
}
if (failed)
break;
for (j = 0; j < num_tput_clnts; i++, j++) {
mt_thread_info[i].s = s;
mt_thread_info[i].cntl_handle = cntl_handle;
mt_thread_info[i].num_workers = &mt_test_num_workers;
mt_thread_info[i].waitqueue = &waitqueue;
mt_thread_info[i].result = &result;
mt_thread_info[i].index = i;
mt_thread_info[i].ch_cfg.num_pkts = num_pkts_tput_ch;
mt_thread_info[i].ch_cfg.tx_delay_ms = 0;
mt_thread_info[i].ch_cfg.data_len = pkt_size_tput_ch;
construct_ch_name(mt_thread_info[i].name, "LL_TPUT_",
i);
INIT_WORK(&mt_thread_info[i].work,
ut_mt_worker_func);
strlcpy(mt_thread_info[i].ch_cfg.ch_name,
mt_thread_info[i].name,
GLINK_NAME_SIZE);
ret = glink_ut_mt_send_config_reqs(s, cntl_handle,
&mt_thread_info[i]);
UT_ASSERT_INT_MT(ret, ==, 0);
}
if (failed)
break;
mt_test_wq = kzalloc(sizeof(struct workqueue_struct *) *
(num_lat_clnts + num_tput_clnts),
GFP_KERNEL);
UT_ASSERT_ERR_PTR_MT(mt_test_wq);
for (i = 0; i < (num_lat_clnts + num_tput_clnts); i++) {
*(mt_test_wq + i) = create_singlethread_workqueue(
mt_thread_info[i].name);
UT_ASSERT_PTR_MT(NULL, !=, &mt_thread_info[i].work);
}
if (failed) {
for (; i > 0; i--)
destroy_workqueue(*(mt_test_wq + i));
kfree(mt_test_wq);
i = num_lat_clnts + num_tput_clnts;
break;
}
for (i = 0; i < (num_lat_clnts + num_tput_clnts); i++)
queue_work(*(mt_test_wq + i), &mt_thread_info[i].work);
GLINK_UT_DBG("%s, Queued the work. Thread: %d\n", __func__,
current->pid);
wait_event_interruptible(waitqueue,
atomic_read(&mt_test_num_workers) == 0);
/* Flush and destroy the work */
for (i = 0; i < (num_lat_clnts + num_tput_clnts); i++) {
flush_workqueue(*(mt_test_wq + i));
destroy_workqueue(*(mt_test_wq + i));
*(mt_test_wq + i) = NULL;
}
kfree(mt_test_wq);
kfree(mt_thread_info);
ret = glink_loopback_close(cntl_handle);
GLINK_UT_DBG("%s, CLOSED control channel. Thread: %d\n",
__func__, current->pid);
if (result)
GLINK_STATUS_MT(s, "%s: Failed\n", __func__);
else
GLINK_STATUS_MT(s, "\tOK\n");
} while (0);
if (failed) {
for (; i > 0; i--) {
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name,
mt_thread_info[i].name, GLINK_NAME_SIZE);
pkt.payload.close.name_len =
strlen(mt_thread_info[i].name);
ret = glink_loopback_send_request(
mt_thread_info[i].data_handle, &pkt, CLOSE,
true);
glink_loopback_close(mt_thread_info[i].data_handle);
}
GLINK_STATUS_MT(s, "%s: Failed\n", __func__);
kfree(mt_thread_info);
glink_loopback_close(cntl_handle);
}
}
void init_smd_trans_notify(struct smd_trans_notify *n)
{
init_completion(&n->connected_completion);
init_completion(&n->local_disconnect_completion);
init_completion(&n->remote_disconnect_completion);
init_completion(&n->tx_done_completion);
init_completion(&n->rx_intent_req_completion);
init_completion(&n->rx_completion);
n->rx_reuse = false;
}
void reset_smd_trans_notify(struct smd_trans_notify *n)
{
reinit_completion(&n->connected_completion);
reinit_completion(&n->local_disconnect_completion);
reinit_completion(&n->remote_disconnect_completion);
reinit_completion(&n->tx_done_completion);
reinit_completion(&n->rx_intent_req_completion);
reinit_completion(&n->rx_completion);
n->rx_reuse = false;
}
void smd_trans_notify_state(void *handle, const void *priv,
unsigned event)
{
struct smd_trans_notify *n = (struct smd_trans_notify *)priv;
if (!n) {
GLINK_UT_ERR("%s: smd_trans_notify not found\n", __func__);
return;
}
switch (event) {
case GLINK_CONNECTED:
complete(&n->connected_completion);
break;
case GLINK_LOCAL_DISCONNECTED:
complete(&n->local_disconnect_completion);
break;
case GLINK_REMOTE_DISCONNECTED:
complete(&n->remote_disconnect_completion);
break;
default:
GLINK_UT_ERR("%s: unrecognized event %d\n", __func__, event);
break;
};
}
void smd_trans_notify_tx_done(void *handle, const void *priv,
const void *pkt_priv, const void *ptr)
{
struct smd_trans_notify *n = (struct smd_trans_notify *)priv;
if (!n) {
GLINK_UT_ERR("%s: smd_trans_notify not found\n", __func__);
return;
}
complete(&n->tx_done_completion);
}
bool smd_trans_rx_intent_req(void *handle, const void *priv, size_t sz)
{
struct smd_trans_notify *n = (struct smd_trans_notify *)priv;
if (!n) {
GLINK_UT_ERR("%s: smd_trans_notify not found\n", __func__);
return false;
}
complete(&n->rx_intent_req_completion);
return true;
}
void smd_trans_notify_rx(void *handle, const void *priv, const void *pkt_priv,
const void *ptr, size_t size)
{
struct smd_trans_notify *n = (struct smd_trans_notify *)priv;
bool do_completion = false;
if (!n) {
GLINK_UT_ERR("%s: smd_trans_notify not found\n", __func__);
return;
}
if ((!pkt_priv && size) || !memcmp(pkt_priv, ptr, size))
do_completion = true;
else
GLINK_UT_ERR(
"%s: received data did not match sent\n", __func__);
glink_rx_done(handle, ptr, n->rx_reuse);
if (do_completion)
complete(&n->rx_completion);
}
/**
* glink_ut0_emd_trans_basic_va() - Basic sanity test using SMD transitional
* transport to MPSS
*
* @s: pointer to output file
*
* This test simulates a simple channel open and close to MPSS over SMD
* transitional transport. Variant A opens the channel then signals the modem
* to create the SMD channel to test the local open first scenario.
*/
static void glink_ut0_smd_trans_basic_va(struct seq_file *s)
{
struct glink_open_config cfg;
void *handle;
struct smd_trans_notify notify;
unsigned long wait_ret;
bool failed = false;
struct glink_ut_dbgfs *ut_dfs_d;
struct glink_dbgfs_data *dfs_d;
if (!(smsm_get_state(SMSM_MODEM_STATE) & (SMSM_INIT | SMSM_SMDINIT))) {
seq_puts(s, "Error: Modem not SMSM and SMD initialized\n");
return;
}
if (smsm_get_state(SMSM_APPS_STATE) & SMSM_SMD_LOOPBACK)
seq_puts(s, "Warning: SMSM signal for loopback already set\n");
init_smd_trans_notify(&notify);
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s\n", ut_dfs_d->ut_name);
memset(&cfg, 0, sizeof(struct glink_open_config));
cfg.priv = &notify;
cfg.options = 0;
cfg.transport = ut_dfs_d->xprt_name;
cfg.edge = ut_dfs_d->edge_name;
cfg.name = "LOOPBACK";
cfg.notify_rx = smd_trans_notify_rx;
cfg.notify_tx_done = smd_trans_notify_tx_done;
cfg.notify_state = smd_trans_notify_state;
cfg.notify_rx_intent_req = smd_trans_rx_intent_req;
reset_smd_trans_notify(&notify);
handle = glink_open(&cfg);
if (IS_ERR(handle)) {
seq_printf(s, "Error: glink_open failed %d\n",
(int)PTR_ERR(handle));
return;
}
smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK);
wait_ret = wait_for_completion_timeout(&notify.connected_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out waiting for open callback\n");
failed = true;
}
glink_close(handle);
wait_ret = wait_for_completion_timeout(
&notify.local_disconnect_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for local close callback\n");
failed = true;
}
if (!failed)
seq_puts(s, "\tOK\n");
}
/**
* glink_ut0_emd_trans_basic_vb() - Basic sanity test using SMD transitional
* transport to MPSS
*
* @s: pointer to output file
*
* This test simulates a simple channel open and close to MPSS over SMD
* transitional transport. Variant B signals the modem to create the SMD
* channel then opens the channel to test the local open last scenario.
*/
static void glink_ut0_smd_trans_basic_vb(struct seq_file *s)
{
struct glink_open_config cfg;
void *handle;
struct smd_trans_notify notify;
struct glink_ut_dbgfs *ut_dfs_d;
struct glink_dbgfs_data *dfs_d;
unsigned long wait_ret;
bool failed = false;
if (!(smsm_get_state(SMSM_MODEM_STATE) & (SMSM_INIT | SMSM_SMDINIT))) {
seq_puts(s, "Error: Modem not SMSM and SMD initialized\n");
return;
}
if (smsm_get_state(SMSM_APPS_STATE) & SMSM_SMD_LOOPBACK)
seq_puts(s, "Warning: SMSM signal for loopback already set\n");
init_smd_trans_notify(&notify);
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s\n", ut_dfs_d->ut_name);
memset(&cfg, 0, sizeof(struct glink_open_config));
cfg.priv = &notify;
cfg.options = 0;
cfg.transport = ut_dfs_d->xprt_name;
cfg.edge = ut_dfs_d->edge_name;
cfg.name = "LOOPBACK";
cfg.notify_rx = smd_trans_notify_rx;
cfg.notify_tx_done = smd_trans_notify_tx_done;
cfg.notify_state = smd_trans_notify_state;
cfg.notify_rx_intent_req = smd_trans_rx_intent_req;
reset_smd_trans_notify(&notify);
smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK);
msleep(500);
handle = glink_open(&cfg);
if (IS_ERR(handle)) {
seq_printf(s, "Error: glink_open failed %d\n",
(int)PTR_ERR(handle));
return;
}
wait_ret = wait_for_completion_timeout(&notify.connected_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out waiting for open callback\n");
failed = true;
}
glink_close(handle);
wait_ret = wait_for_completion_timeout(
&notify.local_disconnect_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for local close callback\n");
failed = true;
}
if (!failed)
seq_puts(s, "\tOK\n");
}
/**
* glink_ut1_smd_trans_tx() - Basic sanity test using SMD transitional
* transport to MPSS
*
* @s: pointer to output file
*
* This test simulates simple packet transmit and receive cases to MPSS over
* SMD transitional transport.
*/
static void glink_ut1_smd_trans_tx(struct seq_file *s)
{
struct glink_open_config cfg;
struct glink_ut_dbgfs *ut_dfs_d;
struct glink_dbgfs_data *dfs_d;
void *handle;
struct smd_trans_notify notify;
unsigned long wait_ret;
bool failed = false;
void *buf;
int ret;
if (!(smsm_get_state(SMSM_MODEM_STATE) & (SMSM_INIT | SMSM_SMDINIT))) {
seq_puts(s, "Error: Modem not SMSM and SMD initialized\n");
return;
}
init_smd_trans_notify(&notify);
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s\n", ut_dfs_d->ut_name);
memset(&cfg, 0, sizeof(struct glink_open_config));
cfg.priv = &notify;
cfg.options = 0;
cfg.transport = ut_dfs_d->xprt_name;
cfg.edge = ut_dfs_d->edge_name;
cfg.name = "LOOPBACK";
cfg.notify_rx = smd_trans_notify_rx;
cfg.notify_tx_done = smd_trans_notify_tx_done;
cfg.notify_state = smd_trans_notify_state;
cfg.notify_rx_intent_req = smd_trans_rx_intent_req;
reset_smd_trans_notify(&notify);
handle = glink_open(&cfg);
if (IS_ERR(handle)) {
seq_printf(s, "Error: glink_open failed %d\n",
(int)PTR_ERR(handle));
return;
}
smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK);
wait_ret = wait_for_completion_timeout(&notify.connected_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out waiting for open callback\n");
failed = true;
}
/* basic tx case - queue rx, then tx, then rx the loopbacked packet */
buf = kmalloc(SZ_4K, GFP_KERNEL);
if (!buf) {
seq_puts(s, "Error: no memory for buffer\n");
goto close;
}
memset(buf, SZ_4K, SZ_4K);
ret = glink_queue_rx_intent(handle, buf, SZ_4K);
if (ret) {
seq_puts(s, "Failed: queue rx intent returned error\n");
failed = true;
goto close;
}
ret = glink_tx(handle, buf, buf, SZ_4K, GLINK_TX_REQ_INTENT);
if (ret) {
seq_puts(s, "Failed: tx returned error\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.tx_done_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for tx done callback\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.rx_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for rx callback\n");
failed = true;
goto close;
}
seq_puts(s, "Case 1 complete\n");
/* adv tx case - tx, get rx req, queue rx, then rx */
reset_smd_trans_notify(&notify);
ret = glink_tx(handle, buf, buf, SZ_4K, GLINK_TX_REQ_INTENT);
if (ret) {
seq_puts(s, "Failed: tx returned error\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.rx_intent_req_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for rx intent req callback\n");
failed = true;
goto close;
}
ret = glink_queue_rx_intent(handle, buf, SZ_4K);
if (ret) {
seq_puts(s, "Failed: queue rx intent returned error\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.tx_done_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for tx done callback\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.rx_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for rx callback\n");
failed = true;
goto close;
}
seq_puts(s, "Case 2 complete\n");
kfree(buf);
close:
glink_close(handle);
wait_ret = wait_for_completion_timeout(
&notify.local_disconnect_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for local close callback\n");
failed = true;
}
if (!failed)
seq_puts(s, "\tOK\n");
}
/**
* glink_ut1_smd_trans_poll_and_mask() - Basic sanity test of poll() and mask()
*
* @s: pointer to output file
*
* This test simulates simple rpm poll() and irq_mask() to MPSS over
* SMD transitional transport.
*/
static void glink_ut1_smd_trans_poll_and_mask(struct seq_file *s)
{
struct glink_open_config cfg;
struct glink_ut_dbgfs *ut_dfs_d;
struct glink_dbgfs_data *dfs_d;
void *handle;
struct smd_trans_notify notify;
unsigned long wait_ret;
bool failed = false;
void *buf;
int ret;
/*
* Need to change the smd trans driver to configure the modem edge as
* intentless, and then comment out these lines
*/
seq_puts(s, "Warning: Modem edge needs to be in intentless config\n");
seq_puts(s, "Passed\n");
return;
if (!(smsm_get_state(SMSM_MODEM_STATE) & (SMSM_INIT | SMSM_SMDINIT))) {
seq_puts(s, "Error: Modem not SMSM and SMD initialized\n");
return;
}
init_smd_trans_notify(&notify);
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s\n", ut_dfs_d->ut_name);
memset(&cfg, 0, sizeof(struct glink_open_config));
cfg.priv = &notify;
cfg.options = 0;
cfg.transport = ut_dfs_d->xprt_name;
cfg.edge = ut_dfs_d->edge_name;
cfg.name = "LOOPBACK";
cfg.notify_rx = smd_trans_notify_rx;
cfg.notify_tx_done = smd_trans_notify_tx_done;
cfg.notify_state = smd_trans_notify_state;
cfg.notify_rx_intent_req = smd_trans_rx_intent_req;
reset_smd_trans_notify(&notify);
handle = glink_open(&cfg);
if (IS_ERR(handle)) {
seq_printf(s, "Error: glink_open failed %d\n",
(int)PTR_ERR(handle));
return;
}
smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK);
wait_ret = wait_for_completion_timeout(&notify.connected_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out waiting for open callback\n");
failed = true;
}
/* basic tx case - queue rx, then tx, then rx the loopbacked packet */
buf = kmalloc(SZ_4K, GFP_KERNEL);
if (!buf) {
seq_puts(s, "Error: no memory for buffer\n");
goto close;
}
memset(buf, SZ_4K, SZ_4K);
ret = glink_queue_rx_intent(handle, buf, SZ_4K);
if (ret) {
seq_puts(s, "Failed: queue rx intent returned error\n");
failed = true;
goto close1;
}
ret = glink_rpm_mask_rx_interrupt(handle, true, NULL);
if (ret) {
seq_printf(s, "Failed: rpm_mask_rx error:%d\n", ret);
failed = true;
goto close1;
}
ret = glink_tx(handle, buf, buf, SZ_4K, GLINK_TX_REQ_INTENT);
if (ret) {
seq_puts(s, "Failed: tx returned error\n");
failed = true;
goto close2;
}
wait_ret = wait_for_completion_timeout(&notify.tx_done_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for tx done callback\n");
failed = true;
goto close2;
}
while (1) {
ret = glink_rpm_rx_poll(handle);
if (ret < 0) {
seq_printf(s, "Failed: poll error:%d\n", ret);
failed = true;
goto close2;
}
if (completion_done(&notify.rx_completion))
break;
}
close2:
glink_rpm_mask_rx_interrupt(handle, false, NULL);
close1:
kfree(buf);
close:
glink_close(handle);
wait_ret = wait_for_completion_timeout(
&notify.local_disconnect_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for local close callback\n");
failed = true;
}
if (!failed)
seq_puts(s, "\tOK\n");
}
/**
* glink_ut1_smd_trans_intentless() - Basic sanity test of intentless operation
*
* @s: pointer to output file
*
* This test simulates simple intentless operation to MPSS over
* SMD transitional transport.
*/
static void glink_ut1_smd_trans_intentless(struct seq_file *s)
{
struct glink_open_config cfg;
struct glink_ut_dbgfs *ut_dfs_d;
struct glink_dbgfs_data *dfs_d;
void *handle;
struct smd_trans_notify notify;
unsigned long wait_ret;
bool failed = false;
void *buf;
int ret;
/*
* Need to change the smd trans driver to configure the modem edge as
* intentless, and then comment out these lines
*/
seq_puts(s, "Warning: Modem edge needs to be in intentless config\n");
seq_puts(s, "Passed\n");
return;
if (!(smsm_get_state(SMSM_MODEM_STATE) & (SMSM_INIT | SMSM_SMDINIT))) {
seq_puts(s, "Error: Modem not SMSM and SMD initialized\n");
return;
}
init_smd_trans_notify(&notify);
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s\n", ut_dfs_d->ut_name);
memset(&cfg, 0, sizeof(struct glink_open_config));
cfg.priv = &notify;
cfg.options = 0;
cfg.transport = ut_dfs_d->xprt_name;
cfg.edge = ut_dfs_d->edge_name;
cfg.name = "LOOPBACK";
cfg.notify_rx = smd_trans_notify_rx;
cfg.notify_tx_done = smd_trans_notify_tx_done;
cfg.notify_state = smd_trans_notify_state;
cfg.notify_rx_intent_req = smd_trans_rx_intent_req;
reset_smd_trans_notify(&notify);
handle = glink_open(&cfg);
if (IS_ERR(handle)) {
seq_printf(s, "Error: glink_open failed %d\n",
(int)PTR_ERR(handle));
return;
}
smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK);
wait_ret = wait_for_completion_timeout(&notify.connected_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out waiting for open callback\n");
failed = true;
}
buf = kmalloc(SZ_4K, GFP_KERNEL);
if (!buf) {
seq_puts(s, "Error: no memory for buffer\n");
goto close;
}
memset(buf, SZ_4K, SZ_4K);
ret = glink_tx(handle, buf, buf, SZ_4K, GLINK_TX_SINGLE_THREADED);
if (ret) {
seq_printf(s, "Failed: tx returned error:%d\n", ret);
failed = true;
goto close1;
}
wait_ret = wait_for_completion_timeout(&notify.tx_done_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for tx done callback\n");
failed = true;
goto close1;
}
wait_ret = wait_for_completion_timeout(&notify.rx_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for rx callback\n");
failed = true;
goto close1;
}
close1:
kfree(buf);
close:
glink_close(handle);
wait_ret = wait_for_completion_timeout(
&notify.local_disconnect_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for local close callback\n");
failed = true;
}
if (!failed)
seq_puts(s, "\tOK\n");
}
/**
* glink_ut1_smd_trans_rx_reuse() - RX intent reuse test using SMD transitional
* transport to MPSS
*
* @s: pointer to output file
*
* This test simulates rx intent reuse to MPSS over SMD transitional transport.
*/
static void glink_ut1_smd_trans_rx_reuse(struct seq_file *s)
{
struct glink_open_config cfg;
struct glink_ut_dbgfs *ut_dfs_d;
struct glink_dbgfs_data *dfs_d;
void *handle;
struct smd_trans_notify notify;
unsigned long wait_ret;
bool failed = false;
void *buf;
int ret;
if (!(smsm_get_state(SMSM_MODEM_STATE) & (SMSM_INIT | SMSM_SMDINIT))) {
seq_puts(s, "Error: Modem not SMSM and SMD initialized\n");
return;
}
init_smd_trans_notify(&notify);
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
GLINK_STATUS(s, "Running %s\n", ut_dfs_d->ut_name);
memset(&cfg, 0, sizeof(struct glink_open_config));
cfg.priv = &notify;
cfg.options = 0;
cfg.transport = ut_dfs_d->xprt_name;
cfg.edge = ut_dfs_d->edge_name;
cfg.name = "LOOPBACK";
cfg.notify_rx = smd_trans_notify_rx;
cfg.notify_tx_done = smd_trans_notify_tx_done;
cfg.notify_state = smd_trans_notify_state;
cfg.notify_rx_intent_req = smd_trans_rx_intent_req;
reset_smd_trans_notify(&notify);
notify.rx_reuse = true;
handle = glink_open(&cfg);
if (IS_ERR(handle)) {
seq_printf(s, "Error: glink_open failed %d\n",
(int)PTR_ERR(handle));
return;
}
smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK);
wait_ret = wait_for_completion_timeout(&notify.connected_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out waiting for open callback\n");
failed = true;
}
buf = kmalloc(SZ_4K, GFP_KERNEL);
if (!buf) {
seq_puts(s, "Error: no memory for buffer\n");
goto close;
}
memset(buf, SZ_4K, SZ_4K);
ret = glink_queue_rx_intent(handle, buf, SZ_4K);
if (ret) {
seq_puts(s, "Failed: queue rx intent returned error\n");
failed = true;
goto close;
}
ret = glink_tx(handle, buf, buf, SZ_4K, GLINK_TX_REQ_INTENT);
if (ret) {
seq_puts(s, "Failed: tx returned error\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.tx_done_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for tx done callback\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.rx_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for rx callback\n");
failed = true;
goto close;
}
reset_smd_trans_notify(&notify);
ret = glink_tx(handle, buf, buf, SZ_4K, GLINK_TX_REQ_INTENT);
if (ret) {
seq_puts(s, "Failed: tx returned error\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.tx_done_completion,
5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for tx done callback\n");
failed = true;
goto close;
}
wait_ret = wait_for_completion_timeout(&notify.rx_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for rx callback\n");
failed = true;
goto close;
}
close:
kfree(buf);
glink_close(handle);
wait_ret = wait_for_completion_timeout(
&notify.local_disconnect_completion, 5 * HZ);
if (!wait_ret) {
seq_puts(s, "Failed: timed out for local close callback\n");
failed = true;
}
if (!failed)
seq_puts(s, "\tOK\n");
}
static void glink_local_mt_core(struct seq_file *s, int xprt_id,
testfunc do_test)
{
void *cntl_handle = NULL;
void *data_handle = NULL;
int failed = 0;
int ret;
do {
ret = do_test(s, cntl_handle, data_handle);
UT_ASSERT_INT(ret, ==, 0);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed)
GLINK_STATUS(s, "%s: Failed\n", __func__);
}
static int glink_ut1_local_mt_test_1_thread(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
struct workqueue_struct *local_mt_test_thread_1_wq;
struct mt_test_info_struct local_mt_thread_1_info;
struct req pkt;
int failed = 0;
int ret = 0;
const char *name = "LL_DATA_CLNT";
char *cntl_xprt = "lloop";
char *cntl_edge = "local";
char *cntl_name = "LOCAL_LOOPBACK_CLNT";
wait_queue_head_t waitqueue;
int result = 0;
init_waitqueue_head(&waitqueue);
GLINK_STATUS_MT(s, "Running %s\n", __func__);
do {
cntl_handle = glink_loopback_open(cntl_xprt, cntl_edge,
cntl_name, CH_CNTL_TYPE, LINEAR_RX, false);
UT_ASSERT_ERR_PTR_MT(cntl_handle);
local_mt_thread_1_info.s = s;
local_mt_thread_1_info.pkt = pkt;
local_mt_thread_1_info.cntl_handle = cntl_handle;
local_mt_thread_1_info.waitqueue = &waitqueue;
local_mt_thread_1_info.result = &result;
strlcpy(local_mt_thread_1_info.name, name,
sizeof(local_mt_thread_1_info.name) + 1);
atomic_set(&local_mt_test_num_workers, 1);
INIT_WORK_ONSTACK(&local_mt_thread_1_info.work,
ut_local_mt_worker_func);
ret = glink_ut1_local_mt_send_config_reqs(s, cntl_handle,
name);
local_mt_test_thread_1_wq =
create_singlethread_workqueue(
"local_mt_test_thread_1_wq");
UT_ASSERT_ERR_PTR_MT(local_mt_test_thread_1_wq);
UT_ASSERT_PTR_MT(NULL, !=, &local_mt_thread_1_info.work);
queue_work(local_mt_test_thread_1_wq,
&local_mt_thread_1_info.work);
GLINK_UT_DBG("%s, Queued the work. Thread: %d\n", __func__,
current->pid);
wait_event_interruptible(waitqueue,
atomic_read(&local_mt_test_num_workers) == 0);
flush_workqueue(local_mt_test_thread_1_wq);
destroy_workqueue(local_mt_test_thread_1_wq);
local_mt_test_thread_1_wq = NULL;
/* Send requests to the server to close each data channel */
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name, strlen(name) + 1);
pkt.payload.close.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
/* Close control channel */
pkt.payload.close.delay_ms = 0;
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s, Sent CLOSE request. Thread: %d\n", __func__,
current->pid);
ret = glink_loopback_close(cntl_handle);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s, Closed the control channel. Thread: %d\n",
__func__, current->pid);
} while (0);
if (failed || result) {
GLINK_STATUS_MT(s, "%s: Failed\n", __func__);
glink_loopback_close(cntl_handle);
ret = 1;
}
return ret;
}
static void glink_ut1_local_mt_1_thread(struct seq_file *s)
{
testfunc do_test;
int xprt_id = LOCAL_LOOPBACK_XPRT;
do_test = glink_ut1_local_mt_test_1_thread;
glink_local_mt_core(s, xprt_id, do_test);
}
static int glink_ut1_local_mt_test_3_threads(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
struct workqueue_struct *local_mt_test_thread_1_wq;
struct workqueue_struct *local_mt_test_thread_2_wq;
struct workqueue_struct *local_mt_test_thread_3_wq;
struct mt_test_info_struct local_mt_thread_1_info;
struct mt_test_info_struct local_mt_thread_2_info;
struct mt_test_info_struct local_mt_thread_3_info;
struct req pkt;
static wait_queue_head_t waitqueue;
int failed = 0;
int ret = 0;
int result = 0;
const char *name1 = "LL_DATA_1_CLNT";
const char *name2 = "LL_DATA_2_CLNT";
const char *name3 = "LL_DATA_3_CLNT";
init_waitqueue_head(&waitqueue);
GLINK_STATUS_MT(s, "Running %s\n", __func__);
do {
/* Open control channel */
cntl_handle = glink_loopback_open("lloop",
"local",
"LOCAL_LOOPBACK_CLNT",
CH_CNTL_TYPE, LINEAR_RX, false);
UT_ASSERT_ERR_PTR_MT(cntl_handle);
atomic_set(&local_mt_test_num_workers, 3);
/* Pack info into workqueue structs */
local_mt_thread_1_info.s = s;
local_mt_thread_1_info.pkt = pkt;
local_mt_thread_1_info.cntl_handle = cntl_handle;
local_mt_thread_1_info.waitqueue = &waitqueue;
local_mt_thread_1_info.result = &result;
strlcpy(local_mt_thread_1_info.name, name1,
sizeof(local_mt_thread_1_info.name) + 1);
INIT_WORK_ONSTACK(&local_mt_thread_1_info.work,
ut_local_mt_worker_func);
ret = glink_ut1_local_mt_send_config_reqs(s, cntl_handle,
name1);
UT_ASSERT_INT_MT(ret, ==, 0);
local_mt_thread_2_info.s = s;
local_mt_thread_2_info.pkt = pkt;
local_mt_thread_2_info.cntl_handle = cntl_handle;
local_mt_thread_2_info.waitqueue = &waitqueue;
local_mt_thread_2_info.result = &result;
strlcpy(local_mt_thread_2_info.name, name2,
sizeof(local_mt_thread_2_info.name) + 1);
INIT_WORK_ONSTACK(&local_mt_thread_2_info.work,
ut_local_mt_worker_func);
ret = glink_ut1_local_mt_send_config_reqs(s, cntl_handle,
name2);
local_mt_thread_3_info.s = s;
local_mt_thread_3_info.pkt = pkt;
local_mt_thread_3_info.cntl_handle = cntl_handle;
local_mt_thread_3_info.waitqueue = &waitqueue;
local_mt_thread_3_info.result = &result;
strlcpy(local_mt_thread_3_info.name, name3,
sizeof(local_mt_thread_3_info.name) + 1);
INIT_WORK_ONSTACK(&local_mt_thread_3_info.work,
ut_local_mt_worker_func);
ret = glink_ut1_local_mt_send_config_reqs(s, cntl_handle,
name3);
/* Create the workqueues */
local_mt_test_thread_1_wq =
create_singlethread_workqueue(
"local_mt_test_thread_1_wq");
UT_ASSERT_ERR_PTR_MT(local_mt_test_thread_1_wq);
UT_ASSERT_PTR_MT(NULL, !=, &local_mt_thread_1_info.work);
local_mt_test_thread_2_wq =
create_singlethread_workqueue(
"local_mt_test_thread_2_wq");
UT_ASSERT_ERR_PTR_MT(local_mt_test_thread_2_wq);
UT_ASSERT_PTR_MT(NULL, !=, &local_mt_thread_2_info.work);
local_mt_test_thread_3_wq =
create_singlethread_workqueue(
"local_mt_test_thread_3_wq");
UT_ASSERT_ERR_PTR_MT(local_mt_test_thread_3_wq);
UT_ASSERT_PTR_MT(NULL, !=, &local_mt_thread_3_info.work);
/* Queue the work */
queue_work(local_mt_test_thread_1_wq,
&local_mt_thread_1_info.work);
queue_work(local_mt_test_thread_2_wq,
&local_mt_thread_2_info.work);
queue_work(local_mt_test_thread_3_wq,
&local_mt_thread_3_info.work);
GLINK_UT_DBG("%s, Queued the work. Thread: %d\n", __func__,
current->pid);
wait_event_interruptible(waitqueue,
atomic_read(&local_mt_test_num_workers) == 0);
/* Flush and destroy the work */
flush_workqueue(local_mt_test_thread_1_wq);
destroy_workqueue(local_mt_test_thread_1_wq);
local_mt_test_thread_1_wq = NULL;
flush_workqueue(local_mt_test_thread_2_wq);
destroy_workqueue(local_mt_test_thread_2_wq);
local_mt_test_thread_2_wq = NULL;
flush_workqueue(local_mt_test_thread_3_wq);
destroy_workqueue(local_mt_test_thread_3_wq);
local_mt_test_thread_3_wq = NULL;
/* Send requests to the server to close each data channel */
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name1, strlen(name1) + 1);
pkt.payload.close.name_len = strlen(name1);
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name2, strlen(name2) + 1);
pkt.payload.close.name_len = strlen(name2);
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name3, strlen(name3) + 1);
pkt.payload.close.name_len = strlen(name3);
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
/* Close the control Channel */
pkt.payload.close.delay_ms = 0;
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s, Sent CLOSE request. Thread: %d\n", __func__,
current->pid);
ret = glink_loopback_close(cntl_handle);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s, CLOSED control channel. Thread: %d\n",
__func__, current->pid);
} while (0);
if (failed || result) {
GLINK_STATUS_MT(s, "%s: Failed\n", __func__);
glink_loopback_close(cntl_handle);
ret = 1;
}
return ret;
}
static int glink_ut1_local_mt_send_config_reqs(struct seq_file *s,
void *cntl_handle, const char *name)
{
struct req pkt;
int ret;
int failed = 0;
int len = strlen(testdata);
struct loopback_channel *lpb_ch =
(struct loopback_channel *)cntl_handle;
const char *cntl_transport = lpb_ch->open_cfg.transport;
const char *cntl_edge = lpb_ch->open_cfg.edge;
const char *cntl_name = lpb_ch->open_cfg.name;
do {
pkt.payload.open.delay_ms = 0;
strlcpy(pkt.payload.open.ch_name, name, strlen(name) + 1);
pkt.payload.open.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt, OPEN,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
pkt.payload.tx_conf.random_delay = 0;
pkt.payload.tx_conf.delay_ms = 0;
pkt.payload.tx_conf.echo_count = 1;
pkt.payload.tx_conf.transform_type = NO_TRANSFORM;
strlcpy(pkt.payload.tx_conf.ch_name, name, strlen(name) + 1);
pkt.payload.tx_conf.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt, TX_CONFIG,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG(
"%s:%s:%s %s: %s %s, thread: %d\n",
cntl_transport, cntl_edge, cntl_name, __func__,
"Sent TX_CONFIG request for data channel",
name, current->pid);
pkt.payload.rx_done_conf.random_delay = 0;
pkt.payload.rx_done_conf.delay_ms = 0;
strlcpy(pkt.payload.rx_done_conf.ch_name, name,
strlen(name) + 1);
pkt.payload.rx_done_conf.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
RX_DONE_CONFIG, true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG(
"%s:%s:%s %s: %s %s, thread: %d\n",
cntl_transport, cntl_edge, cntl_name, __func__,
name,
"Sent RX_DONE_CONFIG request for data channel",
current->pid);
GLINK_UT_DBG("%s: start for iterations[%d]\n", __func__,
ut_iterations);
pkt.payload.q_rx_int_conf.random_delay = 0;
pkt.payload.q_rx_int_conf.delay_ms = 0;
pkt.payload.q_rx_int_conf.num_intents = ut_iterations;
pkt.payload.q_rx_int_conf.intent_size = len;
strlcpy(pkt.payload.q_rx_int_conf.ch_name, name,
strlen(name) + 1);
pkt.payload.q_rx_int_conf.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
QUEUE_RX_INTENT_CONFIG,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG(
"%s:%s:%s %s: %s %s, thread: %d\n",
cntl_transport, cntl_edge, cntl_name, __func__,
"Sent QUEUE_RX_INTENT_CONFIG request for data channel",
name, current->pid);
} while (0);
if (failed)
ret = 1;
return ret;
}
static void glink_ut1_local_mt_3_threads(struct seq_file *s)
{
testfunc do_test;
int xprt_id = LOCAL_LOOPBACK_XPRT;
do_test = glink_ut1_local_mt_test_3_threads;
glink_local_mt_core(s, xprt_id, do_test);
}
static void ut_local_mt_worker_func(struct work_struct *work)
{
int failed = 0;
int ret, i;
int len = strlen(testdata);
void *cntl_handle;
void *data_handle = NULL;
struct req pkt;
struct seq_file *s;
struct mt_test_info_struct *local_mt_thread_info;
const char *transport = "lloop";
const char *edge = "local";
const char *name;
local_mt_thread_info = container_of(work,
struct mt_test_info_struct, work);
cntl_handle = local_mt_thread_info->cntl_handle;
s = local_mt_thread_info->s;
pkt = local_mt_thread_info->pkt;
name = local_mt_thread_info->name;
do {
/* Open data channel on client */
data_handle = glink_loopback_open(transport, edge, name,
CH_DATA_TYPE, LINEAR_RX, false);
UT_ASSERT_ERR_PTR_MT(data_handle);
GLINK_UT_DBG(
"%s:%s:%s %s: called glink_loopack_open() thread: %d\n",
transport, edge, name, __func__, current->pid);
for (i = 0; i < ut_iterations; i++) {
if (i == 0)
ret = glink_loopback_tx(data_handle,
(void *)testdata, len, true,
true, false);
else
ret = glink_loopback_tx(data_handle,
(void *)testdata, len, false,
false, false);
UT_ASSERT_INT_MT(ret, ==, 0);
if (failed)
break;
GLINK_UT_DBG(
"%s:%s:%s %s: %s iteration: %d, thread: %d\n",
transport, edge, name, __func__,
"Called glink_loopback_tx(), ",
i, current->pid);
}
if (failed)
break;
/* Close the data handle, but not the control handle */
ret = glink_loopback_close(data_handle);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG(
"%s: Before dec, local_mt_test_num_workers == %d. Thread: %d\n",
__func__,
atomic_read(&local_mt_test_num_workers),
current->pid);
atomic_dec(&local_mt_test_num_workers);
GLINK_UT_DBG(
"%s: After dec, local_mt_test_num_workers == %d. Thread: %d\n",
__func__,
atomic_read(&local_mt_test_num_workers),
current->pid);
wake_up(local_mt_thread_info->waitqueue);
} while (0);
if (failed) {
GLINK_STATUS_MT(s, "%s: Failed\n", __func__);
ret = glink_loopback_close(data_handle);
*local_mt_thread_info->result = 1;
GLINK_UT_DBG(
"%s: Before dec, local_mt_test_num_workers == %d. Thread: %d\n",
__func__,
atomic_read(&local_mt_test_num_workers),
current->pid);
atomic_dec(&local_mt_test_num_workers);
GLINK_UT_DBG(
"%s: After dec, local_mt_test_num_workers == %d. Thread: %d\n",
__func__,
atomic_read(&local_mt_test_num_workers),
current->pid);
GLINK_UT_DBG("%s:%s:%s %s: Closed channel, thread: %d\n",
transport, edge, name,
__func__, current->pid);
wake_up(local_mt_thread_info->waitqueue);
}
}
/**
* glink_mpss_mt_core - The MPSS-SMEM core multithreaded test core function.
*
* @s: pointer to ouptut file
* @do_test: The function containing the test to run
*
* Tests that use this core function simulate multithreaded data exchange.
* There is a single-threaded and multithreaded variety.
*/
static void glink_mpss_mt_core(struct seq_file *s)
{
struct glink_dbgfs_data *dfs_d;
struct glink_ut_dbgfs *ut_dfs_d;
int failed = 0;
int ret;
char *cntl_ch_name;
char *data_ch_name;
void *cntl_handle = NULL;
const char *cntl_ch_pfix = "LOOPBACK_CTL_";
const char *data_ch_pfix = "LOOPBACK_DATA_";
unsigned int cntl_ch_len = GLINK_NAME_SIZE + strlen(cntl_ch_pfix);
unsigned int data_ch_len = GLINK_NAME_SIZE + strlen(data_ch_pfix);
char edge[GLINK_NAME_SIZE] = {0};
dfs_d = s->private;
ut_dfs_d = dfs_d->priv_data;
glink_ut_to_upper(edge, ut_dfs_d->edge_name);
cntl_ch_name = kzalloc(cntl_ch_len,
GFP_KERNEL);
if (!cntl_ch_name) {
GLINK_STATUS(s, "%s:control channel name Allocation failed\n",
__func__);
return;
}
snprintf(cntl_ch_name, cntl_ch_len,
"%s%s", cntl_ch_pfix, edge);
data_ch_name = kzalloc(data_ch_len,
GFP_KERNEL);
if (!data_ch_name) {
GLINK_STATUS(s, "%s:Data channel name Allocation failed\n",
__func__);
kfree(cntl_ch_name);
return;
}
snprintf(data_ch_name, data_ch_len,
"%s%s", data_ch_pfix, edge);
do {
cntl_handle = glink_loopback_open(ut_dfs_d->xprt_name,
ut_dfs_d->edge_name,
cntl_ch_name, CH_CNTL_TYPE, LINEAR_RX, false);
UT_ASSERT_ERR_PTR_MT(cntl_handle);
ret = ut_dfs_d->tfunc(s, cntl_handle, (void **)&data_ch_name);
UT_ASSERT_INT(ret, ==, 0);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed)
GLINK_STATUS(s, "%s: Failed\n", __func__);
kfree(data_ch_name);
kfree(cntl_ch_name);
}
/**
* glink_ut1_mpss_mt_test_1_thread - The single-threaded MPSS-SMEM test.
*
* @s: pointer to output file
* @cntl_handle: pointer to the control channel structure
* @data_handle: pointer to the data channel structure
*
* This test configures the loopback server and sends ut_iterations messages on
* to the modem on a separate workqueue.
*/
static int glink_ut1_mpss_mt_test_1_thread(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
struct workqueue_struct *mpss_mt_test_thread_1_wq;
struct mt_test_info_struct mpss_mt_thread_1_info;
struct req pkt;
int failed = 0;
int ret = 0;
bool wq_created = false;
const char *name = (const char *)*data_handle;
wait_queue_head_t waitqueue;
int result = 0;
init_waitqueue_head(&waitqueue);
GLINK_STATUS_MT(s, "Running %s\n", __func__);
do {
mpss_mt_thread_1_info.s = s;
mpss_mt_thread_1_info.pkt = pkt;
mpss_mt_thread_1_info.cntl_handle = cntl_handle;
mpss_mt_thread_1_info.waitqueue = &waitqueue;
mpss_mt_thread_1_info.result = &result;
strlcpy(mpss_mt_thread_1_info.name, name,
sizeof(mpss_mt_thread_1_info.name) + 1);
atomic_set(&mpss_mt_test_num_workers, 1);
INIT_WORK_ONSTACK(&mpss_mt_thread_1_info.work,
ut_mpss_mt_worker_func);
ret = glink_ut1_mpss_mt_send_config_reqs(s, cntl_handle,
name, &mpss_mt_thread_1_info);
UT_ASSERT_INT_MT(ret, ==, 0);
mpss_mt_test_thread_1_wq =
create_singlethread_workqueue(
"mpss_mt_test_thread_1_wq");
UT_ASSERT_ERR_PTR_MT(mpss_mt_test_thread_1_wq);
UT_ASSERT_PTR_MT(NULL, !=, &mpss_mt_thread_1_info.work);
wq_created = true;
queue_work(mpss_mt_test_thread_1_wq,
&mpss_mt_thread_1_info.work);
GLINK_UT_DBG("%s, Queued the work. Thread: %d\n", __func__,
current->pid);
wait_event_interruptible(waitqueue,
atomic_read(&mpss_mt_test_num_workers) == 0);
flush_workqueue(mpss_mt_test_thread_1_wq);
destroy_workqueue(mpss_mt_test_thread_1_wq);
mpss_mt_test_thread_1_wq = NULL;
/* Send requests to the server to close each data channel */
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name, strlen(name) + 1);
pkt.payload.close.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
ret = glink_loopback_close(cntl_handle);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s, Closed the control channel. Thread: %d\n",
__func__, current->pid);
} while (0);
if (failed || result) {
GLINK_STATUS_MT(s, "%s: Failed\n", __func__);
glink_loopback_close(cntl_handle);
if (wq_created) {
flush_workqueue(mpss_mt_test_thread_1_wq);
destroy_workqueue(mpss_mt_test_thread_1_wq);
}
ret = 1;
}
return ret;
}
/**
* glink_ut1_mpss_mt_test_3_threads - The multi-threaded MPSS-SMEM test.
*
* @s: pointer to output file
* @cntl_handle: pointer to the control channel structure
* @data_handle: pointer to the data channel structure
*
* This test configures the loopback server and sends ut_iterations messages on
* to the modem on 3 separate single-threaded workqueues. ut_iterations messages
* are sent on each workqueue.
*/
static int glink_ut1_mpss_mt_test_3_threads(struct seq_file *s,
void *cntl_handle, void **data_handle)
{
struct workqueue_struct *mpss_mt_test_thread_1_wq;
struct workqueue_struct *mpss_mt_test_thread_2_wq;
struct workqueue_struct *mpss_mt_test_thread_3_wq;
struct mt_test_info_struct mpss_mt_thread_1_info;
struct mt_test_info_struct mpss_mt_thread_2_info;
struct mt_test_info_struct mpss_mt_thread_3_info;
struct req pkt;
static wait_queue_head_t waitqueue;
int failed = 0;
int ret = 0;
int result = 0;
char *name1;
char *name2;
char *name3;
const char *data_ch_name = (const char *)*data_handle;
unsigned int data_ch_len = strlen(data_ch_name) + 3;
name1 = kzalloc(data_ch_len, GFP_KERNEL);
name2 = kzalloc(data_ch_len, GFP_KERNEL);
name3 = kzalloc(data_ch_len, GFP_KERNEL);
if (!name1 || !name2 || !name3) {
GLINK_STATUS_MT(s, "Allocation Failed %s\n", __func__);
kfree(name1);
kfree(name2);
kfree(name3);
return -ENOMEM;
}
snprintf(name1, data_ch_len, "%s%s", data_ch_name, "_1");
snprintf(name2, data_ch_len, "%s%s", data_ch_name, "_2");
snprintf(name3, data_ch_len, "%s%s", data_ch_name, "_3");
init_waitqueue_head(&waitqueue);
GLINK_STATUS_MT(s, "Running %s\n", __func__);
do {
/* Open control channel */
atomic_set(&mpss_mt_test_num_workers, 3);
/* Pack info into workqueue structs */
mpss_mt_thread_1_info.s = s;
mpss_mt_thread_1_info.pkt = pkt;
mpss_mt_thread_1_info.cntl_handle = cntl_handle;
mpss_mt_thread_1_info.waitqueue = &waitqueue;
mpss_mt_thread_1_info.result = &result;
strlcpy(mpss_mt_thread_1_info.name, name1,
sizeof(mpss_mt_thread_1_info.name) + 1);
INIT_WORK_ONSTACK(&mpss_mt_thread_1_info.work,
ut_mpss_mt_worker_func);
ret = glink_ut1_mpss_mt_send_config_reqs(s, cntl_handle,
name1, &mpss_mt_thread_1_info);
UT_ASSERT_INT_MT(ret, ==, 0);
mpss_mt_thread_2_info.s = s;
mpss_mt_thread_2_info.pkt = pkt;
mpss_mt_thread_2_info.cntl_handle = cntl_handle;
mpss_mt_thread_2_info.waitqueue = &waitqueue;
mpss_mt_thread_2_info.result = &result;
strlcpy(mpss_mt_thread_2_info.name, name2,
sizeof(mpss_mt_thread_2_info.name) + 1);
INIT_WORK_ONSTACK(&mpss_mt_thread_2_info.work,
ut_mpss_mt_worker_func);
ret = glink_ut1_mpss_mt_send_config_reqs(s, cntl_handle,
name2, &mpss_mt_thread_2_info);
UT_ASSERT_INT_MT(ret, ==, 0);
mpss_mt_thread_3_info.s = s;
mpss_mt_thread_3_info.pkt = pkt;
mpss_mt_thread_3_info.cntl_handle = cntl_handle;
mpss_mt_thread_3_info.waitqueue = &waitqueue;
mpss_mt_thread_3_info.result = &result;
strlcpy(mpss_mt_thread_3_info.name, name3,
sizeof(mpss_mt_thread_3_info.name) + 1);
INIT_WORK_ONSTACK(&mpss_mt_thread_3_info.work,
ut_mpss_mt_worker_func);
ret = glink_ut1_mpss_mt_send_config_reqs(s, cntl_handle,
name3, &mpss_mt_thread_3_info);
UT_ASSERT_INT_MT(ret, ==, 0);
/* Create the workqueues */
mpss_mt_test_thread_1_wq =
create_singlethread_workqueue(
"mpss_mt_test_thread_1_wq");
UT_ASSERT_ERR_PTR_MT(mpss_mt_test_thread_1_wq);
UT_ASSERT_PTR_MT(NULL, !=, &mpss_mt_thread_1_info.work);
mpss_mt_test_thread_2_wq =
create_singlethread_workqueue(
"mpss_mt_test_thread_2_wq");
UT_ASSERT_ERR_PTR_MT(mpss_mt_test_thread_2_wq);
UT_ASSERT_PTR_MT(NULL, !=, &mpss_mt_thread_2_info.work);
mpss_mt_test_thread_3_wq =
create_singlethread_workqueue(
"mpss_mt_test_thread_3_wq");
UT_ASSERT_ERR_PTR_MT(mpss_mt_test_thread_3_wq);
UT_ASSERT_PTR_MT(NULL, !=, &mpss_mt_thread_3_info.work);
/* Queue the work */
queue_work(mpss_mt_test_thread_1_wq,
&mpss_mt_thread_1_info.work);
queue_work(mpss_mt_test_thread_2_wq,
&mpss_mt_thread_2_info.work);
queue_work(mpss_mt_test_thread_3_wq,
&mpss_mt_thread_3_info.work);
GLINK_UT_DBG("%s, Queued the work. Thread: %d\n", __func__,
current->pid);
wait_event_interruptible(waitqueue,
atomic_read(&mpss_mt_test_num_workers) == 0);
/* Flush and destroy the work */
flush_workqueue(mpss_mt_test_thread_1_wq);
destroy_workqueue(mpss_mt_test_thread_1_wq);
mpss_mt_test_thread_1_wq = NULL;
flush_workqueue(mpss_mt_test_thread_2_wq);
destroy_workqueue(mpss_mt_test_thread_2_wq);
mpss_mt_test_thread_2_wq = NULL;
flush_workqueue(mpss_mt_test_thread_3_wq);
destroy_workqueue(mpss_mt_test_thread_3_wq);
mpss_mt_test_thread_3_wq = NULL;
/* Send requests to the server to close each data channel */
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name1, strlen(name1) + 1);
pkt.payload.close.name_len = strlen(name1);
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name2, strlen(name2) + 1);
pkt.payload.close.name_len = strlen(name2);
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
pkt.payload.close.delay_ms = 0;
strlcpy(pkt.payload.close.ch_name, name3, strlen(name3) + 1);
pkt.payload.close.name_len = strlen(name3);
ret = glink_loopback_send_request(cntl_handle, &pkt, CLOSE,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
ret = glink_loopback_close(cntl_handle);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG("%s, CLOSED control channel. Thread: %d\n",
__func__, current->pid);
} while (0);
if (failed || result) {
GLINK_STATUS_MT(s, "%s: Failed\n", __func__);
glink_loopback_close(cntl_handle);
ret = 1;
}
kfree(name1);
kfree(name2);
kfree(name3);
return ret;
}
/**
* glink_ut1_mpss_mt_send_config_reqs - Send loopback server configuration
* requests.
*
* @s: pointer to output file
* @cntl_handle: pointer to the control channel structure
* @name: name of the data channel
* @mpss_mt_thread_info: information particular to the channel being configured
*
* This function sends configuration requests to the loopback server on the
* modem, and opens the data channel. It does the following actions in the
* following order: send OPEN configuration request, send TX_CONFIG request,
* send RX_DONE_CONFIG request, open the data channel, send
* QUEUE_RX_INTENT_CONFIG request.
*/
static int glink_ut1_mpss_mt_send_config_reqs(struct seq_file *s,
void *cntl_handle, const char *name,
struct mt_test_info_struct *mpss_mt_thread_info)
{
struct req pkt;
int ret;
int failed = 0;
int len = strlen(testdata);
void *data_handle = NULL;
struct loopback_channel *lpb_ch =
(struct loopback_channel *)cntl_handle;
const char *cntl_transport = lpb_ch->open_cfg.transport;
const char *cntl_edge = lpb_ch->open_cfg.edge;
const char *cntl_name = lpb_ch->open_cfg.name;
do {
pkt.payload.open.delay_ms = 0;
strlcpy(pkt.payload.open.ch_name, name, strlen(name) + 1);
pkt.payload.open.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt, OPEN,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
pkt.payload.tx_conf.random_delay = 0;
pkt.payload.tx_conf.delay_ms = 0;
pkt.payload.tx_conf.echo_count = 1;
pkt.payload.tx_conf.transform_type = NO_TRANSFORM;
strlcpy(pkt.payload.tx_conf.ch_name, name, strlen(name) + 1);
pkt.payload.tx_conf.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt, TX_CONFIG,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG(
"%s:%s:%s %s: %s %s, thread: %d\n",
cntl_transport, cntl_edge, cntl_name, __func__,
"Sent TX_CONFIG request for data channel",
name, current->pid);
pkt.payload.rx_done_conf.random_delay = 0;
pkt.payload.rx_done_conf.delay_ms = 0;
strlcpy(pkt.payload.rx_done_conf.ch_name, name,
strlen(name) + 1);
pkt.payload.rx_done_conf.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
RX_DONE_CONFIG, true);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG(
"%s:%s:%s %s: %s %s, thread: %d\n",
cntl_transport, cntl_edge, cntl_name, __func__,
name,
"Sent RX_DONE_CONFIG request for data channel",
current->pid);
/*
* Open data channel here, before sending the
* QUEUE_RX_INTENT_CONFIG request. Pass the opened data channel
* to the worker function through the structure you created.
* This is necessary because the modem loopback server doesn not
* defer QUEUE_RX_INTENT_CONFIG requests until after the channel
* is fully opened. Therefore it is necessary to open the
* channel before sending this request.
*/
data_handle = glink_loopback_open(cntl_transport, cntl_edge,
name, CH_DATA_TYPE, LINEAR_RX, false);
UT_ASSERT_ERR_PTR_MT(data_handle);
mpss_mt_thread_info->data_handle = data_handle;
pkt.payload.q_rx_int_conf.random_delay = 0;
pkt.payload.q_rx_int_conf.delay_ms = 0;
pkt.payload.q_rx_int_conf.num_intents = ut_iterations;
pkt.payload.q_rx_int_conf.intent_size = len;
strlcpy(pkt.payload.q_rx_int_conf.ch_name, name,
strlen(name) + 1);
pkt.payload.q_rx_int_conf.name_len = strlen(name);
ret = glink_loopback_send_request(cntl_handle, &pkt,
QUEUE_RX_INTENT_CONFIG,
true);
UT_ASSERT_INT_MT(ret, ==, 0);
} while (0);
if (failed)
ret = 1;
return ret;
}
/**
* ut1_mpss_mt_worker_func - Worker function for the MPSS-SMEM multithreaded
* tests.
*
* @work: Pointer to work structure
*
* The worker function for the MPSS multi-threaded tests. Sends ut_iterations
* messages to the remote side.
*/
static void ut_mpss_mt_worker_func(struct work_struct *work)
{
int failed = 0;
int ret, i;
int len = strlen(testdata);
void *cntl_handle;
void *data_handle = NULL;
struct req pkt;
struct seq_file *s;
struct mt_test_info_struct *mpss_mt_thread_info;
struct loopback_channel *lpb_ch;
const char *transport;
const char *edge;
const char *name;
mpss_mt_thread_info = container_of(work,
struct mt_test_info_struct, work);
cntl_handle = mpss_mt_thread_info->cntl_handle;
data_handle = mpss_mt_thread_info->data_handle;
s = mpss_mt_thread_info->s;
pkt = mpss_mt_thread_info->pkt;
name = mpss_mt_thread_info->name;
lpb_ch = (struct loopback_channel *)cntl_handle;
transport = lpb_ch->open_cfg.transport;
edge = lpb_ch->open_cfg.edge;
do {
for (i = 0; i < ut_iterations; i++) {
if (i == 0)
ret = glink_loopback_tx(data_handle,
(void *)testdata, len, true,
true, false);
else
ret = glink_loopback_tx(data_handle,
(void *)testdata, len, false,
true, false);
UT_ASSERT_INT_MT(ret, ==, 0);
if (failed)
break;
GLINK_UT_DBG(
"%s:%s:%s %s: %s iteration: %d, thread: %d\n",
transport, edge, name, __func__,
"Called glink_loopback_tx(), ",
i, current->pid);
}
if (failed)
break;
/* Close the data handle, but not the control handle */
ret = glink_loopback_close(data_handle);
UT_ASSERT_INT_MT(ret, ==, 0);
GLINK_UT_DBG(
"%s: Before dec, mpss_mt_test_num_workers == %d. Thread: %d\n",
__func__,
atomic_read(&mpss_mt_test_num_workers),
current->pid);
atomic_dec(&mpss_mt_test_num_workers);
GLINK_UT_DBG(
"%s: After dec, mpss_mt_test_num_workers == %d. Thread: %d\n",
__func__,
atomic_read(&mpss_mt_test_num_workers),
current->pid);
wake_up(mpss_mt_thread_info->waitqueue);
} while (0);
if (failed) {
GLINK_STATUS_MT(s, "%s: Failed\n", __func__);
ret = glink_loopback_close(data_handle);
*mpss_mt_thread_info->result = 1;
GLINK_UT_DBG(
"%s: Before dec, mpss_mt_test_num_workers == %d. Thread: %d\n",
__func__,
atomic_read(&mpss_mt_test_num_workers),
current->pid);
atomic_dec(&mpss_mt_test_num_workers);
GLINK_UT_DBG(
"%s: After dec, mpss_mt_test_num_workers == %d. Thread: %d\n",
__func__,
atomic_read(&mpss_mt_test_num_workers),
current->pid);
GLINK_UT_DBG("%s:%s:%s %s: Closed channel, thread: %d\n",
transport, edge, name,
__func__, current->pid);
wake_up(mpss_mt_thread_info->waitqueue);
}
}
/* Locking test */
static void channel_test_thread_worker(struct work_struct *work);
struct channel_test_info_struct {
struct seq_file *s;
int channel_id;
char channel_name[GLINK_NAME_SIZE];
struct work_struct work;
};
static void glink_ut1_mock_channel_locking_test_1_thread(struct seq_file *s)
{
int failed = 0;
struct glink_mock_xprt *mock_ptr;
struct workqueue_struct *channel_test_thread_1_wq = NULL;
struct channel_test_info_struct channel_test_thread_1_info;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
channel_test_thread_1_info.s = s;
channel_test_thread_1_info.channel_id = 1;
strlcpy(channel_test_thread_1_info.channel_name, "loopback1",
sizeof(channel_test_thread_1_info.channel_name));
INIT_WORK_ONSTACK(&channel_test_thread_1_info.work,
channel_test_thread_worker);
channel_test_thread_1_wq =
create_singlethread_workqueue(
"channel_test_thread_1_wq");
UT_ASSERT_PTR(NULL, !=, channel_test_thread_1_wq);
queue_work(channel_test_thread_1_wq,
&channel_test_thread_1_info.work);
flush_workqueue(channel_test_thread_1_wq);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed)
GLINK_STATUS(s, "\tFailed\n");
if (channel_test_thread_1_wq != NULL)
destroy_workqueue(channel_test_thread_1_wq);
}
static void glink_ut1_mock_channel_locking_test_3_threads(struct seq_file *s)
{
int failed = 0;
struct glink_mock_xprt *mock_ptr;
struct workqueue_struct *channel_test_thread_1_wq;
struct workqueue_struct *channel_test_thread_2_wq;
struct workqueue_struct *channel_test_thread_3_wq;
struct channel_test_info_struct channel_test_thread_1_info;
struct channel_test_info_struct channel_test_thread_2_info;
struct channel_test_info_struct channel_test_thread_3_info;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
mock_ptr = mock_xprt_get(MOCK);
mock_xprt_reset(mock_ptr);
UT_ASSERT_INT(0, ==, do_mock_negotiation(s, 0x1, 0x0, MOCK));
channel_test_thread_1_info.s = s;
channel_test_thread_1_info.channel_id = 1;
strlcpy(channel_test_thread_1_info.channel_name, "loopback1",
sizeof("loopback1"));
INIT_WORK_ONSTACK(&channel_test_thread_1_info.work,
channel_test_thread_worker);
channel_test_thread_2_info.s = s;
channel_test_thread_2_info.channel_id = 2;
strlcpy(channel_test_thread_2_info.channel_name, "loopback2",
sizeof("loopback2"));
INIT_WORK_ONSTACK(&channel_test_thread_2_info.work,
channel_test_thread_worker);
channel_test_thread_3_info.s = s;
channel_test_thread_3_info.channel_id = 3;
strlcpy(channel_test_thread_3_info.channel_name, "loopback3",
sizeof("loopback3"));
INIT_WORK_ONSTACK(&channel_test_thread_3_info.work,
channel_test_thread_worker);
channel_test_thread_1_wq =
create_singlethread_workqueue(
"channel_test_thread_1_wq");
UT_ASSERT_PTR(NULL, !=, channel_test_thread_1_wq);
channel_test_thread_2_wq =
create_singlethread_workqueue(
"channel_test_thread_2_wq");
UT_ASSERT_PTR(NULL, !=, channel_test_thread_2_wq);
channel_test_thread_3_wq =
create_singlethread_workqueue(
"channel_test_thread_3_wq");
UT_ASSERT_PTR(NULL, !=, channel_test_thread_3_wq);
queue_work(channel_test_thread_1_wq,
&channel_test_thread_1_info.work);
queue_work(channel_test_thread_2_wq,
&channel_test_thread_2_info.work);
queue_work(channel_test_thread_3_wq,
&channel_test_thread_3_info.work);
flush_workqueue(channel_test_thread_1_wq);
destroy_workqueue(channel_test_thread_1_wq);
channel_test_thread_1_wq = NULL;
flush_workqueue(channel_test_thread_2_wq);
destroy_workqueue(channel_test_thread_2_wq);
channel_test_thread_2_wq = NULL;
flush_workqueue(channel_test_thread_3_wq);
destroy_workqueue(channel_test_thread_3_wq);
channel_test_thread_3_wq = NULL;
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed)
GLINK_STATUS(s, "\tFailed\n");
}
/**
* struct rwref_lock_container - rwref_lock testing context
* @rwref_lock: lock being tested
* @released: lock release function called
*
* @write_work: write worker structure
* @write_worker_event: write work has done something
* @write_wq_started: write work has started
* @write_wq_lock_count: num of times worker acquired write lock
* @write_wq_ended: write worker has terminated
*
* @read_work: read worker structure
* @read_worker_event: read work has done something
* @read_wq_started: read work has started
* @read_wq_lock_count: num of times worker acquired read lock
* @read_wq_ended: read worker has terminated
*/
struct rwref_lock_container {
struct rwref_lock rwref_lock;
bool released;
struct work_struct write_work;
struct completion write_worker_event;
bool write_wq_started;
unsigned write_wq_lock_count;
bool write_wq_ended;
struct work_struct read_work;
struct completion read_worker_event;
bool read_wq_started;
unsigned read_wq_lock_count;
bool read_wq_ended;
};
/**
* ut0_rwref_release() - rwref_lock free function
* @rwref: pointer to lock structure
*/
static void ut0_rwref_release(struct rwref_lock *rwref)
{
struct rwref_lock_container *data_ptr;
data_ptr = container_of(rwref, struct rwref_lock_container, rwref_lock);
if (data_ptr)
data_ptr->released = true;
}
/**
* ut0_rwref_write_worker() - rwref_lock write worker
*
* Used to acquire the write lock in a separate execution context from main
* thread.
*/
static void ut0_rwref_write_worker(struct work_struct *work)
{
struct rwref_lock_container *rwref_ptr;
rwref_ptr = container_of(work, struct rwref_lock_container, write_work);
if (rwref_ptr == NULL)
return;
rwref_ptr->write_wq_started = true;
complete(&rwref_ptr->write_worker_event);
rwref_write_get(&rwref_ptr->rwref_lock);
rwref_ptr->write_wq_lock_count++;
complete(&rwref_ptr->write_worker_event);
rwref_write_put(&rwref_ptr->rwref_lock);
rwref_ptr->write_wq_ended = true;
complete(&rwref_ptr->write_worker_event);
}
/**
* glink_ut0_rwref_lock() - verify rwref lock
*
* @s: pointer to output file
*/
static void glink_ut0_rwref_lock(struct seq_file *s)
{
int failed = 0;
struct workqueue_struct *write_wq = NULL;
struct workqueue_struct *read_wq = NULL;
struct rwref_lock_container rwref_obj;
GLINK_STATUS(s, "Running %s\n", __func__);
do {
memset(&rwref_obj, 0x00, sizeof(rwref_obj));
rwref_lock_init(&rwref_obj.rwref_lock, ut0_rwref_release);
write_wq = create_singlethread_workqueue("ut0_rwref_write_wq");
UT_ASSERT_PTR(NULL, !=, write_wq);
INIT_WORK_ONSTACK(&rwref_obj.write_work,
ut0_rwref_write_worker);
init_completion(&rwref_obj.write_worker_event);
read_wq = create_singlethread_workqueue("ut0_rwref_read_wq");
UT_ASSERT_PTR(NULL, !=, read_wq);
INIT_WORK_ONSTACK(&rwref_obj.read_work, ut0_rwref_write_worker);
init_completion(&rwref_obj.read_worker_event);
/* verify read can be grabbed multiple times */
UT_ASSERT_INT(0, ==, rwref_obj.rwref_lock.read_count);
rwref_read_get(&rwref_obj.rwref_lock);
UT_ASSERT_INT(1, ==, rwref_obj.rwref_lock.read_count);
rwref_read_get(&rwref_obj.rwref_lock);
UT_ASSERT_INT(2, ==, rwref_obj.rwref_lock.read_count);
/* trigger workqueue to grab write lock */
queue_work(write_wq, &rwref_obj.write_work);
wait_for_completion_timeout(&rwref_obj.write_worker_event, HZ);
UT_ASSERT_INT(true, ==, rwref_obj.write_wq_started);
UT_ASSERT_INT(0, ==, rwref_obj.write_wq_lock_count);
/* release read locks and confirm write lock is acquired */
rwref_read_put(&rwref_obj.rwref_lock);
UT_ASSERT_INT(1, ==, rwref_obj.rwref_lock.read_count);
UT_ASSERT_INT(0, ==, rwref_obj.write_wq_lock_count);
rwref_read_put(&rwref_obj.rwref_lock);
wait_for_completion_timeout(&rwref_obj.write_worker_event, HZ);
UT_ASSERT_INT(0, ==, rwref_obj.rwref_lock.read_count);
UT_ASSERT_INT(1, ==, rwref_obj.write_wq_lock_count);
UT_ASSERT_INT(true, ==, rwref_obj.write_wq_ended);
/* put ref with active read - release should not happen */
rwref_read_get(&rwref_obj.rwref_lock);
UT_ASSERT_INT(1, ==, rwref_obj.rwref_lock.read_count);
rwref_put(&rwref_obj.rwref_lock);
UT_ASSERT_INT(false, ==, rwref_obj.released);
rwref_read_put(&rwref_obj.rwref_lock);
UT_ASSERT_INT(0, ==, rwref_obj.rwref_lock.read_count);
UT_ASSERT_INT(true, ==, rwref_obj.released);
/* put ref with active write - release should not happen */
rwref_obj.released = false;
rwref_get(&rwref_obj.rwref_lock);
rwref_write_get(&rwref_obj.rwref_lock);
UT_ASSERT_INT(1, ==, rwref_obj.rwref_lock.write_count);
rwref_put(&rwref_obj.rwref_lock);
UT_ASSERT_INT(false, ==, rwref_obj.released);
rwref_write_put(&rwref_obj.rwref_lock);
UT_ASSERT_INT(0, ==, rwref_obj.rwref_lock.write_count);
UT_ASSERT_INT(true, ==, rwref_obj.released);
GLINK_STATUS(s, "\tOK\n");
} while (0);
if (failed)
GLINK_STATUS(s, "\tFailed\n");
if (write_wq) {
flush_workqueue(write_wq);
destroy_workqueue(write_wq);
}
if (read_wq) {
flush_workqueue(read_wq);
destroy_workqueue(read_wq);
}
}
static void channel_test_thread_worker(struct work_struct *work)
{
int failed = 0;
int ret;
int ITERATIONS = 50;
int i = 0;
int completion_result = 0;
int completion_iter_count = 0;
uint32_t channel_id;
uint32_t received_intent_id;
void *handle = NULL;
char channel_name[GLINK_NAME_SIZE];
struct glink_mock_xprt *mock_ptr;
struct glink_open_config open_cfg;
struct glink_mock_cmd *tx_cmd;
struct glink_core_rx_intent *rx_intent_ptr;
struct glink_mock_rx_intent *intent;
struct glink_mock_tx_data *tx_data;
struct seq_file *s;
struct completion event;
struct channel_test_info_struct *test_thread_work_info;
struct ut_notify_data cb_data;
test_thread_work_info = container_of(work,
struct channel_test_info_struct, work);
channel_id = test_thread_work_info->channel_id;
mutex_lock(&multithread_test_mutex_lha0);
s = test_thread_work_info->s;
mutex_unlock(&multithread_test_mutex_lha0);
strlcpy(channel_name, test_thread_work_info->channel_name,
sizeof(test_thread_work_info->channel_name));
cb_data_init(&cb_data);
GLINK_STATUS_MT(s, "\n\tRunning %s, thread %d, channel_name: %s\n",
__func__, current->pid, channel_name);
do {
/*
* Initialize mock transport and open channel. No
* mock_xprt_reset() call is needed here as the transport was
* re-initialized in the glink_ut_mock_channel_locking_test()
* function before this work was scheduled to run.
*/
mock_ptr = mock_xprt_get(MOCK);
init_completion(&event);
cb_data_reset(&cb_data);
register_completion(&mock_ptr->if_ptr, &event);
/* Open the channel */
memset(&open_cfg, 0, sizeof(struct glink_open_config));
open_cfg.transport = "mock";
open_cfg.edge = "local";
open_cfg.name = channel_name;
open_cfg.notify_rx = glink_test_notify_rx;
open_cfg.notify_tx_done = glink_test_notify_tx_done;
open_cfg.notify_state = glink_test_notify_state;
open_cfg.notify_rx_intent_req = glink_test_rmt_rx_intent_req_cb;
open_cfg.priv = &cb_data;
handle = glink_open(&open_cfg);
UT_ASSERT_ERR_PTR_MT(handle);
channel_id = glink_get_channel_id_for_handle(handle);
tx_cmd = mock_xprt_get_next_cmd_by_cid(channel_id, &event,
mock_ptr);
UT_ASSERT_PTR_MT(NULL, !=, tx_cmd);
UT_ASSERT_INT_MT(LOCAL_OPEN, ==, tx_cmd->type);
UT_ASSERT_INT_MT(channel_id, ==, tx_cmd->local_open.lcid);
UT_ASSERT_STRING_COMPARE_MT(channel_name,
tx_cmd->local_open.name);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_open_ack(
&mock_ptr->if_ptr, tx_cmd->local_open.lcid,
MOCK_XPRT_ID);
kfree(tx_cmd);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_open(
&mock_ptr->if_ptr, channel_id,
channel_name, MOCK_XPRT_ID);
tx_cmd = mock_xprt_get_next_cmd_by_cid(channel_id, &event,
mock_ptr);
UT_ASSERT_PTR_MT(NULL, !=, tx_cmd);
UT_ASSERT_INT_MT(REMOTE_OPEN_ACK, ==, tx_cmd->type);
UT_ASSERT_INT_MT(channel_id, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT_MT(GLINK_CONNECTED, ==, cb_data.event);
kfree(tx_cmd);
/* open channel completed */
while (i < ITERATIONS) {
/*
* Read the data [hello GLINK]
*/
glink_queue_rx_intent(handle, (void *)&cb_data,
strlen(testdata));
intent = mock_xprt_get_intent_by_cid(channel_id,
&event, mock_ptr);
received_intent_id = intent->liid;
UT_ASSERT_PTR_MT(NULL, !=, intent);
UT_ASSERT_INT_MT(strlen(testdata), ==, intent->size);
rx_intent_ptr =
mock_ptr->if_ptr.glink_core_if_ptr->rx_get_pkt_ctx(
&mock_ptr->if_ptr, channel_id, intent->liid);
UT_ASSERT_PTR(NULL, ==, (void *)rx_intent_ptr->data);
rx_intent_ptr->data = (void *)testdata;
rx_intent_ptr->pkt_size = strlen(testdata);
mock_ptr->if_ptr.glink_core_if_ptr->rx_put_pkt_ctx(
&mock_ptr->if_ptr, channel_id, rx_intent_ptr,
true);
UT_ASSERT_INT_MT(true, ==, cb_data.rx_notify);
UT_ASSERT_INT_MT(strlen(testdata), ==, cb_data.size);
kfree(intent);
/*
* Write the data [Hello GLINK]
*/
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_remote_rx_intent_put(
&mock_ptr->if_ptr, channel_id,
received_intent_id, strlen(testdata));
ret = glink_tx(handle, (void *)&cb_data, (void
*)testdata, strlen(testdata),
0);
UT_ASSERT_INT_MT(ret, ==, 0);
completion_result = (int)(HZ / 2);
tx_data = NULL;
while (tx_data == NULL) {
if (completion_result > 0) {
completion_result = (int)
wait_for_completion_timeout(
&event,
completion_result);
} else {
failed = 1;
break;
}
tx_data = mock_xprt_get_tx_data_by_cid(
channel_id, &event, mock_ptr);
completion_iter_count++;
}
UT_ASSERT_PTR_MT(NULL, !=, tx_data);
UT_ASSERT_STRING_COMPARE_MT(testdata, tx_data->data);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_tx_done(
&mock_ptr->if_ptr,
channel_id,
received_intent_id,
false);
UT_ASSERT_INT_MT(true, == , cb_data.tx_done);
++i;
}
/* When the unit testing macros run "break;", they are intended
* to break out of the unit testing while loop. However, in the
* above case they only break out of the test iterations while
* loop. Thus we need to check if "failed" is set, and if so
* break out of the unit testing while loop.
*/
if (failed)
break;
ret = glink_close(handle);
UT_ASSERT_INT_MT(ret, ==, 0);
tx_cmd = mock_xprt_get_next_cmd_by_cid(channel_id, &event,
mock_ptr);
UT_ASSERT_PTR_MT(NULL, !=, tx_cmd);
UT_ASSERT_INT_MT(LOCAL_CLOSE, ==, tx_cmd->type);
UT_ASSERT_INT_MT(channel_id, ==, tx_cmd->local_close.lcid);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_close_ack(
&mock_ptr->if_ptr, tx_cmd->local_close.lcid);
kfree(tx_cmd);
UT_ASSERT_INT_MT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
mock_ptr->if_ptr.glink_core_if_ptr->rx_cmd_ch_remote_close(
&mock_ptr->if_ptr, channel_id);
tx_cmd = mock_xprt_get_next_cmd_by_cid(channel_id, &event,
mock_ptr);
UT_ASSERT_PTR_MT(NULL, !=, tx_cmd);
UT_ASSERT_INT_MT(REMOTE_CLOSE_ACK, ==, tx_cmd->type);
UT_ASSERT_INT_MT(channel_id, ==, tx_cmd->remote_open_ack.rcid);
UT_ASSERT_INT_MT(GLINK_LOCAL_DISCONNECTED, ==, cb_data.event);
kfree(tx_cmd);
GLINK_STATUS_MT(s,
"\tChannel test thread %d, channel %d completed successfully\n",
current->pid, channel_id);
} while (0);
unregister_completion(&event, mock_ptr);
if (failed) {
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
GLINK_STATUS_MT(s,
"\tFailed channel test thread %d, channel: %d\n",
current->pid, channel_id);
if (handle != NULL) {
glink_close(handle);
handle = NULL;
}
}
}
void glink_ut_rss_debug_create(const char *ut_name, const char *xprt_name,
const char *edge_name, testfunc tfunc,
void (*show)(struct seq_file *)) {
struct glink_dbgfs dfs;
struct glink_ut_dbgfs *dbgfs_ut_d;
char dir_name[2*GLINK_NAME_SIZE];
dbgfs_ut_d = kzalloc(sizeof(struct glink_ut_dbgfs), GFP_KERNEL);
if (dbgfs_ut_d == NULL)
return;
strlcpy(dbgfs_ut_d->edge_name, edge_name, GLINK_NAME_SIZE);
strlcpy(dbgfs_ut_d->xprt_name, xprt_name, GLINK_NAME_SIZE);
dbgfs_ut_d->ut_name = ut_name;
dbgfs_ut_d->tfunc = tfunc;
snprintf(dir_name, 2*GLINK_NAME_SIZE, "%s_%s", dbgfs_ut_d->edge_name,
dbgfs_ut_d->xprt_name);
dfs.curr_name = dir_name;
dfs.par_name = "test";
dfs.b_dir_create = false;
glink_debugfs_create(ut_name, show, &dfs, (void *)dbgfs_ut_d, true);
}
void glink_ut_dbgfs_worker_func(struct work_struct *work)
{
struct glink_dbgfs ut_dbgfs;
const char *xprt_name;
const char *edge_name;
char dir_name[2*GLINK_NAME_SIZE];
struct glink_ut_dbgfs_work *dfs_work =
container_of(work, struct glink_ut_dbgfs_work, dbgfs_work);
ut_dbgfs.curr_name = "test";
ut_dbgfs.par_name = "glink";
ut_dbgfs.b_dir_create = true;
xprt_name = dfs_work->xprt;
edge_name = dfs_work->edge;
snprintf(dir_name, 2*GLINK_NAME_SIZE, "%s_%s", edge_name, xprt_name);
glink_debugfs_create(dir_name, NULL, &ut_dbgfs, NULL, false);
/*
* Add Unit Test entries.
*
* The idea with unit tests is that you can run all of them
* from ADB shell by doing:
* adb shell
* cat ut*
*
* And if particular tests fail, you can then repeatedly run the
* failing tests as you debug and resolve the failing test.
*
* unit test names: ut[level no]_edge_test
* unit test levels: 0, 1, 2
* level 0: Basic tests
* level 1: functional tests
* level 2: performance tests
*/
if (!strcmp(xprt_name, "smem")) {
glink_ut_rss_debug_create("ut0_smem_basic",
xprt_name, edge_name,
ut_loopback_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ssr_basic",
xprt_name, edge_name, NULL,
glink_ut0_ssr);
glink_ut_rss_debug_create("ft_ssr_bypass",
xprt_name, edge_name, NULL,
glink_ft_ssr_bypass);
glink_ut_rss_debug_create("ut0_tracer_pkt", xprt_name,
edge_name,
ut_loopback_tp,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut1_smem_open_close",
xprt_name, edge_name,
ut_loopback_open_close_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut1_smem_queue_rx_intent",
xprt_name, edge_name,
ut_loopback_queue_rx_intent_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut2_smem_stress", xprt_name,
edge_name,
ut_loopback_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut2_smem_performance",
xprt_name, edge_name,
ut_loopback_performance_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut1_smem_mt_1_thread",
xprt_name, edge_name,
glink_ut1_mpss_mt_test_1_thread,
glink_mpss_mt_core);
glink_ut_rss_debug_create("ut1_smem_mt_3_threads",
xprt_name, edge_name,
glink_ut1_mpss_mt_test_3_threads,
glink_mpss_mt_core);
glink_ut_rss_debug_create("ut1_smem_rx_reuse",
xprt_name, edge_name,
ut_loopback_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ft_transport_teardown",
xprt_name, edge_name,
NULL,
glink_ft_transport_teardown);
} else if (!strcmp(xprt_name, "smd_trans")
&& !(strcmp(edge_name, "mpss"))) {
/*below test cases are designed to work only with MPSS */
glink_ut_rss_debug_create("ut0_smd_trans_migration_1",
xprt_name, edge_name, NULL,
glink_ut0_smd_trans_migration_1);
glink_ut_rss_debug_create("ut0_smd_trans_basic_va",
xprt_name, edge_name, NULL,
glink_ut0_smd_trans_basic_va);
glink_ut_rss_debug_create("ut0_smd_trans_basic_vb",
xprt_name, edge_name, NULL,
glink_ut0_smd_trans_basic_vb);
glink_ut_rss_debug_create("ut1_smd_trans_tx", xprt_name,
edge_name, NULL,
glink_ut1_smd_trans_tx);
glink_ut_rss_debug_create("ut1_smd_trans_poll_and_mask",
xprt_name, edge_name, NULL,
glink_ut1_smd_trans_poll_and_mask);
glink_ut_rss_debug_create("ut1_smd_trans_intentless",
xprt_name, edge_name, NULL,
glink_ut1_smd_trans_intentless);
glink_ut_rss_debug_create("ut1_smd_trans_rx_reuse",
xprt_name, edge_name, NULL,
glink_ut1_smd_trans_rx_reuse);
} else if (!strcmp(xprt_name, "lloop")) {
glink_ut_rss_debug_create("ut0_basic", xprt_name,
edge_name,
ut_loopback_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut0_sigs", xprt_name,
edge_name,
ut_loopback_sigs,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut0_tracer_pkt", xprt_name,
edge_name,
ut_loopback_tp,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut1_open_close", xprt_name,
edge_name,
ut_loopback_open_close_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut1_queue_rx_intent",
xprt_name, edge_name,
ut_loopback_queue_rx_intent_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut1_tx_with_req_intent",
xprt_name, edge_name,
ut_loopback_tx_with_req_intent_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut2_stress", xprt_name,
edge_name,
ut_loopback_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut2_performance", xprt_name,
edge_name,
ut_loopback_performance_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut0_skb_basic", xprt_name,
edge_name,
ut_loopback_skb_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut2_skb_stress", xprt_name,
edge_name,
ut_loopback_skb_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut0_skb_tx_linear_rx_basic",
xprt_name,
edge_name,
ut_loopback_skb_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut2_skb_tx_linear_rx_stress",
xprt_name,
edge_name,
ut_loopback_skb_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut1_rx_reuse",
xprt_name, edge_name,
ut_loopback_stress_test,
glink_ut_local_basic_core);
glink_ut_rss_debug_create("ut2_mt_perf_test",
xprt_name,
edge_name,
NULL,
glink_ut_mt_test_core);
glink_ut_rss_debug_create("ut1_mt_1_thread",
xprt_name, edge_name,
NULL,
glink_ut1_local_mt_1_thread);
glink_ut_rss_debug_create("ut1_mt_3_threads",
xprt_name, edge_name,
NULL,
glink_ut1_local_mt_3_threads);
} else if (!strcmp(xprt_name, "mock")) {
glink_ut_rss_debug_create("ut0_basic",
xprt_name, edge_name,
NULL,
glink_ut0_mock_basic);
glink_ut_rss_debug_create("ut0_remote_negotiation",
xprt_name, edge_name,
NULL,
glink_ut0_mock_remote_negotiation);
glink_ut_rss_debug_create("ut0_mock_migration_1",
xprt_name, edge_name,
NULL,
glink_ut0_mock_migration_1);
glink_ut_rss_debug_create("ut0_mock_migration_2",
xprt_name, edge_name,
NULL,
glink_ut0_mock_migration_2);
glink_ut_rss_debug_create("ut1_size_greater_than",
xprt_name, edge_name,
NULL,
glink_ut1_mock_size_greater_than);
glink_ut_rss_debug_create("ut1_transmit_no_rx_intent",
xprt_name, edge_name,
NULL,
glink_ut1_mock_transmit_no_rx_intent);
glink_ut_rss_debug_create("ut1_read_no_rx_intent",
xprt_name, edge_name,
NULL,
glink_ut1_mock_read_no_rx_intent);
glink_ut_rss_debug_create("ut1_rmt_rx_intent_req",
xprt_name, edge_name,
NULL,
glink_ut1_mock_rmt_rx_intent_req);
glink_ut_rss_debug_create("ut1_rx_intent_req",
xprt_name, edge_name,
NULL,
glink_ut1_mock_rx_intent_req);
glink_ut_rss_debug_create("ut1_channel_locking_test_1_thread",
xprt_name, edge_name,
NULL,
glink_ut1_mock_channel_locking_test_1_thread);
glink_ut_rss_debug_create("ut1_channel_locking_test_3_threads",
xprt_name, edge_name,
NULL,
glink_ut1_mock_channel_locking_test_3_threads);
glink_ut_rss_debug_create("ut1_open_close",
xprt_name, edge_name,
NULL,
glink_ut1_mock_open_close);
glink_ut_rss_debug_create("ut1_open_close_remote_first",
xprt_name, edge_name,
NULL,
glink_ut1_mock_open_close_remote_first);
glink_ut_rss_debug_create("ut0_rwref_lock",
xprt_name, edge_name,
NULL,
glink_ut0_rwref_lock);
glink_ut_rss_debug_create("ut0_ssr",
xprt_name, edge_name,
NULL,
glink_ut0_mock_ssr);
} else if (!strcmp(xprt_name, "mock_low")) {
glink_ut_rss_debug_create("ut0_low_basic",
xprt_name, edge_name,
NULL,
glink_ut0_mock_low_basic);
} else if (!strcmp(xprt_name, "mock_high")) {
glink_ut_rss_debug_create("ut0_high_basic",
xprt_name, edge_name,
NULL,
glink_ut0_mock_high_basic);
}
kfree(dfs_work);
}
void glink_ut_link_state_cb(struct glink_link_state_cb_info *cb_info,
void *priv)
{
struct glink_ut_dbgfs_work *ut_dfs_work;
if (!cb_info)
return;
ut_dfs_work = kzalloc(sizeof(struct glink_ut_dbgfs_work), GFP_KERNEL);
if (!ut_dfs_work)
return;
ut_dfs_work->xprt = cb_info->transport;
ut_dfs_work->edge = cb_info->edge;
if (cb_info->link_state == GLINK_LINK_STATE_UP) {
INIT_WORK(&ut_dfs_work->dbgfs_work, glink_ut_dbgfs_worker_func);
queue_work(ut_dbgfs_create_workqueue, &ut_dfs_work->dbgfs_work);
}
}
void glink_ut_debug_remove(void)
{
struct glink_dbgfs ut_rm_dbgfs;
ut_rm_dbgfs.curr_name = "test";
ut_rm_dbgfs.par_name = "glink";
glink_debugfs_remove_recur(&ut_rm_dbgfs);
}
static int __init glink_init(void)
{
struct glink_link_info xprt_link_notif = { NULL, NULL,
glink_ut_link_state_cb};
struct glink_dbgfs ut_dbgfs;
ut_dbgfs.curr_name = "glink";
ut_dbgfs.par_name = "root";
ut_dbgfs.b_dir_create = true;
glink_debugfs_create("test", NULL, &ut_dbgfs, NULL, false);
ut_dbgfs_create_workqueue =
create_singlethread_workqueue("dbgfs_queue");
if (!ut_dbgfs_create_workqueue)
return -ENOMEM;
/* Initialize mutex for multithreaded locking test */
mutex_init(&multithread_test_mutex_lha0);
glink_mock_xprt_init();
do_mock_negotiation_init(0x1, 0x0, MOCK_LOW);
do_mock_negotiation_init(0x1, 0x0, MOCK);
do_mock_negotiation_init(0x1, 0x0, MOCK_HIGH);
glink_loopback_xprt_init();
glink_loopback_client_init();
glink_ut_link_state_notif_handle = glink_register_link_state_cb(
&xprt_link_notif, NULL);
return 0;
}
static void glink_exit(void)
{
glink_unregister_link_state_cb(glink_ut_link_state_notif_handle);
flush_workqueue(ut_dbgfs_create_workqueue);
glink_ut_debug_remove();
destroy_workqueue(ut_dbgfs_create_workqueue);
glink_mock_xprt_exit();
glink_loopback_xprt_exit();
glink_loopback_client_exit();
}
module_init(glink_init);
module_exit(glink_exit);
MODULE_DESCRIPTION("G-Link Unit Test");
MODULE_LICENSE("GPL v2");