/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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(¬ify); 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 = ¬ify; 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(¬ify); 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(¬ify.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( ¬ify.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(¬ify); 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 = ¬ify; 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(¬ify); 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(¬ify.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( ¬ify.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(¬ify); 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 = ¬ify; 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(¬ify); 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(¬ify.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(¬ify.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(¬ify.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(¬ify); 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(¬ify.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(¬ify.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(¬ify.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( ¬ify.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(¬ify); 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 = ¬ify; 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(¬ify); 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(¬ify.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(¬ify.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(¬ify.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( ¬ify.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(¬ify); 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 = ¬ify; 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(¬ify); 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(¬ify.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(¬ify.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(¬ify.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( ¬ify.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(¬ify); 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 = ¬ify; 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(¬ify); 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(¬ify.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(¬ify.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(¬ify.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(¬ify); 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(¬ify.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(¬ify.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( ¬ify.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");