// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include #include #include #include "ipclite_test.h" struct kobject *sysfs_dir; static int threads_started, threads_completed, cores_completed; static bool ssr_complete; /* data_lock spinlock is used to increment ping counters in thread safe manner. * core_wq to ensure all the cores have completed the test before next step. * ssr_wq to wait during ssr operation. * reply_wq to wait on replies to ping sent. * thread_wq to wait on all threads local to APPS to complete * test_done is a completion barrier which ensures test case is completed * crash_done is a completion barrier which ensures ssr crash is completed */ DEFINE_SPINLOCK(data_lock); DECLARE_WAIT_QUEUE_HEAD(core_wq); DECLARE_WAIT_QUEUE_HEAD(ssr_wq); DECLARE_WAIT_QUEUE_HEAD(reply_wq); DECLARE_WAIT_QUEUE_HEAD(thread_wq); DECLARE_COMPLETION(test_done); DECLARE_COMPLETION(crash_done); static struct ipclite_thread_data wakeup_check, bg_pings; static struct ipclite_thread_data thread_data; struct handle_t *handle_ptr; static int handle_data[512]; static struct ipclite_test_data *data; static void init_test_params(void) { data->test_params.wait = 1; data->test_params.num_pings = 1000; data->test_params.num_itr = 1; data->test_params.selected_senders = 1; data->test_params.selected_receivers = 1; data->test_params.enabled_cores = IPCLITE_TEST_ALL_CORES; data->test_params.selected_test_case = 0; data->test_params.num_thread = 1; data->test_params.num_senders = 1; data->test_params.num_receivers = 1; } /* Function to pack the different fields into one 64 bit message value * 1 byte header of constant patter 01010101 * 1 byte to store the parameter type * 1 byte to store the test case id * 3 bytes to store the value of parameter in payload * 1 byte to store test start/stop information * 1 byte to store test pass/fail information */ static uint64_t get_param_macro(uint64_t parameter_info, uint64_t test_info, uint64_t payload_info, uint64_t start_stop_info, uint64_t pass_fail_info) { uint64_t param_macro = 0; parameter_info &= GENMASK_ULL(7, 0); test_info &= GENMASK_ULL(7, 0); payload_info &= GENMASK_ULL(23, 0); start_stop_info &= GENMASK_ULL(7, 0); pass_fail_info &= GENMASK_ULL(7, 0); param_macro = ((uint64_t)IPCLITE_TEST_HEADER) << 56; param_macro |= parameter_info << 48; param_macro |= test_info << 40; param_macro |= payload_info << 16; param_macro |= start_stop_info << 8; param_macro |= pass_fail_info; return param_macro; } static inline bool is_enabled_core(int core_id) { return (data->test_params.enabled_cores & BIT(core_id)) ? true : false; } static inline bool is_selected_receiver(int core_id) { return (data->test_params.selected_receivers & BIT(core_id)) ? true : false; } static inline bool is_selected_sender(int core_id) { return (data->test_params.selected_senders & BIT(core_id)) ? true : false; } static void ping_receive(struct ipclite_test_data *data) { pr_debug("Successfully received a ping\n"); data->pings_received[data->client_id]++; wake_up_interruptible(&reply_wq); } static int check_pings(struct ipclite_test_data *data) { for (int i = 0; i < IPCMEM_NUM_HOSTS; ++i) { if (!is_selected_receiver(i)) continue; if (data->pings_sent[i] != data->pings_received[i]) return -IPCLITE_TEST_FAIL; } return 0; } static void ping_all_enabled_cores(u64 msg) { for (int i = 0; i < IPCMEM_NUM_HOSTS; ++i) { if (i == IPCMEM_APPS || !is_enabled_core(i)) continue; ipclite_test_msg_send(i, msg); } } static void ping_sel_senders(uint64_t msg) { for (int i = 0; i < IPCMEM_NUM_HOSTS; ++i) { if (i == IPCMEM_APPS || !(data->test_params.selected_senders & BIT(i))) continue; ipclite_test_msg_send(i, msg); } } static int thread_init(struct ipclite_thread_data *th_data, void *data_ptr, void *fptr) { th_data->data = data_ptr; th_data->run = false; init_waitqueue_head(&th_data->wq); th_data->thread = kthread_run(fptr, th_data, "test thread"); if (IS_ERR(th_data->thread)) { pr_err("Thread creation failed\n"); return -EINVAL; } return 0; } static int ping_selected_receivers(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; struct ipclite_test_data *data = t_data->data; int ret = 0; uint64_t macro_to_ping = get_param_macro(TEST_CASE, data->test_params.selected_test_case, PING_SEND, 0, 0); bool fail = false; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; for (int i = 0; i < data->test_params.num_pings/data->test_params.num_thread; ++i) { for (int j = 0; j < IPCMEM_NUM_HOSTS; ++j) { if (!is_selected_receiver(j)) continue; ret = ipclite_test_msg_send(j, macro_to_ping); if (ret == 0) { spin_lock(&data_lock); data->pings_sent[j]++; spin_unlock(&data_lock); } else fail = true; /* If wait is enabled and number of pings to wait on is sent, * Wait for replies or timeout */ if (data->test_params.wait != 0 && (i+1) % data->test_params.wait == 0) { ret = wait_event_interruptible_timeout(reply_wq, check_pings(data) == 0, msecs_to_jiffies(1000)); if (ret < 1) pr_err("Timeout occurred\n"); } } } pr_debug("Completed iteration. Marking thread as completed\n"); spin_lock(&data_lock); threads_completed++; wake_up_interruptible(&thread_wq); spin_unlock(&data_lock); } return fail ? -IPCLITE_TEST_FAIL : 0; } static int negative_tests(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; int ret = 0, fail = 0; uint64_t param; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; pr_info("Test 1: Sending messages to disabled cores\n"); for (int i = 0; i < IPCMEM_NUM_HOSTS; ++i) { if (!is_selected_receiver(i)) continue; param = get_param_macro(TEST_CASE, NEGATIVE, PING_SEND, 0, 0); ret = ipclite_test_msg_send(i, param); if (ret == 0) { pr_err("TEST FAILED\n"); fail++; } } if (!fail) pr_info("TEST PASSED\n"); pr_info("Test 2: Passing NULL to get_global_parition_info\n"); ret = get_global_partition_info(NULL); if (ret == 0) { pr_err("TEST FAILED\n"); fail++; } else pr_info("TEST PASSED\n"); if (fail != 0) pr_err("Negative TEST FAILED\n"); else pr_info("Negative TEST PASSED\n"); param = get_param_macro(TEST_CASE, NEGATIVE, 0, IPCLITE_TEST_STOP, 0); ipclite_test_msg_send(IPCMEM_APPS, param); wait_event_interruptible_timeout(core_wq, cores_completed == data->test_params.num_senders, msecs_to_jiffies(1000)); complete(&test_done); } return fail == 0 ? 0 : -IPCLITE_TEST_FAIL; } static int hw_unlock_test(void *hw_mutex_byte) { int ret = 0; uint64_t param; if (!hw_mutex_byte) { pr_err("Byte for hardware mutex testing is not initialized.\n"); return -EFAULT; } pr_info("Testing HW Mutex Lock Acquire Functionality\n"); *((int *)(hw_mutex_byte)) = -1; pr_debug("The initial value of the byte is %d\n", *((int *)(hw_mutex_byte))); pr_debug("Locking the mutex from APPS Side\n"); ret = ipclite_hw_mutex_acquire(); if (ret != 0) { pr_err("Could not acquire hw mutex from APPS side\n"); return ret; } pr_debug("Setting the value of the byte to %d\n", IPCMEM_APPS); *((int *)(hw_mutex_byte)) = IPCMEM_APPS; pr_debug("The new value of the byte is %d\n", *((int *)(hw_mutex_byte))); for (int i = 0; i < IPCMEM_NUM_HOSTS; ++i) { if (i == IPCMEM_APPS || !is_selected_receiver(i)) continue; pr_debug("Pinging %s to try and release the locked mutex\n", core_name[i]); param = get_param_macro(TEST_CASE, HW_MUTEX, HW_MUTEX_RELEASE, IPCLITE_TEST_START, 0); ipclite_test_msg_send(i, param); // Wait for timeout here udelay(1000); } if (*((int *)(hw_mutex_byte)) != IPCMEM_APPS) return -IPCLITE_TEST_FAIL; ret = ipclite_hw_mutex_release(); if (ret != 0) pr_err("Could not release mutex lock successfully\n"); return ret; } static int hw_mutex_test(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; struct ipclite_test_data *data = t_data->data; int ret = 0; void *addr = data->global_memory->virt_base; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; ret = hw_unlock_test(addr); if (ret == 0) pr_info("HW Unlock Test Passed.\n"); else pr_info("HW Unlock Test Failed.\n"); complete(&test_done); } return ret; } /* Ping cores which are not selected for ssr in the background */ static int send_bg_pings(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; struct ipclite_test_data *data = t_data->data; int ret; uint64_t param; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; while (!ssr_complete && !kthread_should_stop()) { for (int i = 0; i < IPCMEM_NUM_HOSTS; ++i) { if (i == data->ssr_client || !is_selected_receiver(i)) continue; param = get_param_macro(TEST_CASE, SSR, PING_SEND, 0, 0); ret = ipclite_test_msg_send(i, param); if (ret != 0) pr_err("Unable to ping core %d\n", i); } wait_event_interruptible_timeout(ssr_wq, ssr_complete, msecs_to_jiffies(1000)); } pr_debug("SSR recovery of core %d completed. Exiting thread\n", data->ssr_client); } return 0; } /* Wait for 30s and then send pings one to by one to see if core wakeup * is completed */ static int ssr_wakeup_check(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; struct ipclite_test_data *data = t_data->data; int count = 0, ret = 0; uint64_t param; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; ssr_complete = false; msleep_interruptible(30000); while (count < 10) { pr_debug("Sent ping number %d to check if wakeup is completed\n", count); param = get_param_macro(TEST_CASE, SSR, SSR_WAKEUP, IPCLITE_TEST_START, 0); ret = ipclite_test_msg_send(data->ssr_client, param); ++count; wait_event_interruptible_timeout(ssr_wq, ssr_complete, msecs_to_jiffies(1000)); } if (count == 10 && !ssr_complete) { pr_info("FW Core wakeup failed.\n"); return -IPCLITE_TEST_FAIL; } pr_info("FW Core wakeup completed successfully.\n"); pr_info("Going for non crashing testing.\n"); param = get_param_macro(TEST_CASE, PING, 0, IPCLITE_TEST_START, 0); ipclite_test_msg_send(data->ssr_client, param); complete(&crash_done); } return 0; } static int ssr_test(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; struct ipclite_test_data *data = t_data->data; uint64_t param = 0; int ret = 0; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; ssr_complete = false; ret = thread_init(&wakeup_check, data, ssr_wakeup_check); if (ret != 0) { pr_err("Thread creation failed\n"); return -EINVAL; } ret = thread_init(&bg_pings, data, send_bg_pings); if (ret != 0) { pr_err("Thread creation failed\n"); kthread_stop(wakeup_check.thread); return -EINVAL; } pr_info("Starting on SSR test for core %d\n", data->ssr_client); memset(data->pings_sent, 0, sizeof(data->pings_sent)); memset(data->pings_received, 0, sizeof(data->pings_received)); param = get_param_macro(TEST_CASE, SSR, SSR_CRASHING, IPCLITE_TEST_START, 0); ipclite_test_msg_send(data->ssr_client, param); wait_for_completion(&crash_done); kthread_stop(wakeup_check.thread); kthread_stop(bg_pings.thread); complete(&test_done); } return 0; } static int inc_byte(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; ipclite_atomic_uint32_t *addr = t_data->data; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; for (int i = 0; i < data->test_params.num_itr; ++i) ipclite_global_atomic_inc(addr); threads_completed++; wake_up_interruptible(&thread_wq); } return 0; } static int dec_byte(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; ipclite_atomic_uint32_t *addr = t_data->data; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; for (int i = 0; i < data->test_params.num_itr; ++i) ipclite_global_atomic_dec(addr); threads_completed++; wake_up_interruptible(&thread_wq); } return 0; } static int global_atomics_test(void *byte, int test_number) { int ret = 0; int total_increment = 0; uint64_t param; bool fail = false; struct ipclite_thread_data ga_t1, ga_t2; if (!byte) { pr_err("Byte not initialized. Test Failed\n"); return -EFAULT; } pr_debug("The initial value of the byte is %x\n", *((int *)byte)); threads_completed = 0; threads_started = 0; switch (test_number) { case GLOBAL_ATOMICS_INC: ret = thread_init(&ga_t1, byte, inc_byte); if (ret != 0) { pr_err("Thread creation failed\n"); return -EINVAL; } ret = thread_init(&ga_t2, byte, inc_byte); if (ret != 0) { pr_err("Thread creation failed\n"); kthread_stop(ga_t1.thread); return -EINVAL; } break; case GLOBAL_ATOMICS_DEC: ret = thread_init(&ga_t1, byte, dec_byte); if (ret != 0) { pr_err("Thread creation failed\n"); return -EINVAL; } ret = thread_init(&ga_t2, byte, dec_byte); if (ret != 0) { pr_err("Thread creation failed\n"); kthread_stop(ga_t1.thread); return -EINVAL; } break; case GLOBAL_ATOMICS_INC_DEC: ret = thread_init(&ga_t1, byte, inc_byte); if (ret != 0) { pr_err("Thread creation failed\n"); return -EINVAL; } ret = thread_init(&ga_t2, byte, dec_byte); if (ret != 0) { pr_err("Thread creation failed\n"); kthread_stop(ga_t1.thread); return -EINVAL; } break; default: pr_err("Wrong input provided\n"); return -EINVAL; } param = get_param_macro(TEST_CASE, GLOBAL_ATOMIC, test_number, IPCLITE_TEST_START, 0); for (int i = 0; i < IPCMEM_NUM_HOSTS; ++i) { if (i == IPCMEM_APPS || !is_selected_receiver(i)) continue; ret = ipclite_test_msg_send(i, param); if (ret == 0) threads_started += 2; } if (is_selected_receiver(IPCMEM_APPS)) { ga_t1.run = true; wake_up_interruptible(&ga_t1.wq); ga_t2.run = true; wake_up_interruptible(&ga_t2.wq); threads_started += 2; } /* Wait for all threads to complete or timeout */ ret = wait_event_interruptible_timeout(thread_wq, threads_started == 2 * data->test_params.num_receivers && threads_completed == 2 * data->test_params.num_receivers, msecs_to_jiffies(1000)); if (ret < 1) pr_err("Threads could not complete successfully\n"); pr_debug("The value of the byte is %x\n", *((int *)byte)); /* Stopping threads if they have not already completed before evaluation */ kthread_stop(ga_t1.thread); kthread_stop(ga_t2.thread); total_increment = 2 * data->test_params.num_receivers * data->test_params.num_itr; switch (test_number) { case GLOBAL_ATOMICS_INC: if (*((int *)byte) == total_increment) pr_info("Increment Successful.\n"); else { pr_err("Increment Failed.\n"); fail = true; } break; case GLOBAL_ATOMICS_DEC: if (*((int *)byte) == 0) pr_info("Decrement Successful\n"); else { pr_err("Decrement Failed\n"); fail = true; } break; case GLOBAL_ATOMICS_INC_DEC: if (*((int *)byte) == 0) pr_info("Increment and Decrement Successful\n"); else { pr_err("Increment and Decrement Failed\n"); fail = true; } break; default: pr_err("Wrong input provided\n"); return -EINVAL; } return fail ? -IPCLITE_TEST_FAIL : 0; } static inline uint32_t bitops_count_trailing_one(uint32_t x) { uint32_t mask = 0; for (int i = 0; i < BITS(ipclite_atomic_uint32_t); i++) { mask = 1 << i; if (!(x & mask)) return i; } return BITS(ipclite_atomic_uint32_t); } /** * @brief Finds the first zero in the bitmap * * @param bmap_addr pointer to bitmap * @param size the size of the bitmap indicated in number of bits * @return uint32_t index of the first zero */ static uint32_t bitops_util_find_first_zero(uint32_t *bmap_addr, uint32_t size) { uint32_t res = 0; for (int i = 0; i * BITS(ipclite_atomic_uint32_t) < size; i++) { if (bmap_addr[i] != ~(uint32_t)0) { res = i * BITS(ipclite_atomic_uint32_t) + bitops_count_trailing_one(bmap_addr[i]); return res < size ? res : size; } } return size; } static int alloc_index(int *bitmap_base) { uint32_t prev = 0, index = 0; do { index = bitops_util_find_first_zero((unsigned int *) bitmap_base, NUM_HANDLES); if (index > NUM_HANDLES) { pr_err("No Memory Error. Exiting\n"); break; } prev = ipclite_global_test_and_set_bit(index % 32, (ipclite_atomic_uint32_t *)(bitmap_base + index/32)); if ((prev & (1UL << (index % 32))) == 0) break; } while (true); return index; } void clear_index(int *bitmap_base, uint32_t index) { uint32_t addr_idx = index/32, ii = index % 32; if (bitmap_base == NULL) { pr_err("Invalid pointer passed\n"); return; } ipclite_global_test_and_clear_bit(ii, (ipclite_atomic_uint32_t *)(bitmap_base + addr_idx)); } static int global_atomics_test_set_clear(struct ipclite_test_data *data) { int index = 0, ret = 0; bool fail = false; uint64_t param; handle_ptr = data->global_memory->virt_base; pr_info("Starting global atomics Test 4. Starting allocation of index\n"); pr_debug("The total number of handles is %d\n", NUM_HANDLES); pr_debug("Global Base : %p\n", handle_ptr); for (int itr = 0; itr < data->test_params.num_itr; itr++) { threads_started = 0; threads_completed = 0; for (int j = 0; j < IPCMEM_NUM_HOSTS; ++j) { if (j == IPCMEM_APPS || !is_selected_receiver(j)) continue; param = get_param_macro(TEST_CASE, GLOBAL_ATOMIC, GLOBAL_ATOMICS_SET_CLR, IPCLITE_TEST_START, 0); ret = ipclite_test_msg_send(j, param); if (ret == 0) threads_started++; } if (is_selected_receiver(IPCMEM_APPS)) { threads_started++; for (int i = 0; i < 512; ++i) { index = alloc_index((int *)handle_ptr); handle_data[i] = index; handle_ptr->handle_data[index] = IPCMEM_APPS; } for (int i = 0; i < 512; ++i) { index = handle_data[i]; if (handle_ptr->handle_data[index] != IPCMEM_APPS) { pr_err("Handle data has been overwritten.\n"); pr_err("This is a bug : Core : %d Index : %d\n", handle_ptr->handle_data[index], index); fail = true; } } for (int i = 0; i < 512; ++i) { index = handle_data[i]; clear_index((int *)handle_ptr, index); } threads_completed++; if (fail) break; } wait_event_interruptible_timeout(thread_wq, threads_started == data->test_params.num_receivers && threads_completed == data->test_params.num_receivers, msecs_to_jiffies(1000)); } if (!fail) pr_info("Global Atomics Set and Clear test passed successfully\n"); return fail ? -IPCLITE_TEST_FAIL : 0; } static int global_atomics_test_wrapper(void *data_ptr) { int result = 0, ret = 0; struct ipclite_thread_data *t_data = data_ptr; struct ipclite_test_data *data = t_data->data; void *addr = data->global_memory->virt_base; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; *((int *)addr) = 0; result = global_atomics_test(addr, GLOBAL_ATOMICS_INC); result &= global_atomics_test(addr, GLOBAL_ATOMICS_DEC); result &= global_atomics_test(addr, GLOBAL_ATOMICS_INC_DEC); result &= global_atomics_test_set_clear(data); if (result != 0) { pr_err("Global Atomics TEST FAILED\n"); ret = -IPCLITE_TEST_FAIL; } else { pr_info("Global Atomics TEST PASSED\n"); ret = 0; } complete(&test_done); } return ret; } static int ping_test(void *data_ptr) { int ret = 0; uint64_t param_macro; struct ipclite_test_data *data = data_ptr; struct ipclite_thread_data th_arr[IPCLITE_TEST_MAX_THREADS]; int count; memset(data->pings_sent, 0, sizeof(data->pings_sent)); memset(data->pings_received, 0, sizeof(data->pings_received)); threads_completed = 0; param_macro = 0; for (count = 0; count < data->test_params.num_thread; ++count) { ret = thread_init(&th_arr[count], data, ping_selected_receivers); if (ret != 0) break; } if (count != data->test_params.num_thread) while (count > 0) { kthread_stop(th_arr[count-1].thread); --count; } if (ret != 0) { pr_err("Threads could not be initialized. Ping Test Failed\n"); return ret; } for (threads_started = 0; threads_started < data->test_params.num_thread; ++threads_started) { th_arr[threads_started].run = true; wake_up_interruptible(&th_arr[threads_started].wq); } ret = wait_event_interruptible_timeout(thread_wq, threads_started == data->test_params.num_thread && threads_completed == data->test_params.num_thread, msecs_to_jiffies(1000) * data->test_params.num_thread); if (ret < 1) { pr_err("Threads not completed successfully. Only completed %d threads\n", threads_completed); return ret; } pr_info("All threads completed successfully.\n"); pr_debug("Going for checking\n"); /*Wait for the queue to get processed before checking if all replies are received*/ if (!data->test_params.wait) msleep_interruptible(1000); ret = check_pings(data); if (ret == 0) pr_debug("All replies received successfully.\n"); else pr_debug("All replies not received successfully.\n"); while (count > 0) { kthread_stop(th_arr[count-1].thread); --count; } param_macro = get_param_macro(TEST_CASE, PING, 0, IPCLITE_TEST_STOP, 0); ipclite_test_msg_send(IPCMEM_APPS, param_macro); return ret; } static int wrapper_ping_test(void *data_ptr) { int ret = 0; uint64_t param_macro; struct ipclite_thread_data *t_data = data_ptr; struct ipclite_test_data *data = t_data->data; while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; for (int i = 0; i < data->test_params.num_itr; ++i) { cores_completed = 0; param_macro = get_param_macro(TEST_CASE, PING, 0, IPCLITE_TEST_START, 0); /* Ping all senders to start sending messages. * If APPS is one of the senders start sending */ ping_sel_senders(param_macro); if (is_selected_sender(IPCMEM_APPS)) ping_test(data); wait_event_interruptible_timeout(core_wq, cores_completed == data->test_params.num_senders, msecs_to_jiffies(1000)); ret = check_pings(data); if (ret != 0) pr_info("Iteration %d of ping test failed\n", i+1); else pr_info("Iteration %d of ping test passed\n", i+1); } if (is_selected_sender(IPCMEM_APPS)) complete(&test_done); } return 0; } static int debug_tests(void *data_ptr) { struct ipclite_thread_data *t_data = data_ptr; uint64_t param; int disabled_core = ffz(data->test_params.enabled_cores); while (!kthread_should_stop()) { wait_event_interruptible(t_data->wq, t_data->run); if (kthread_should_stop()) break; t_data->run = false; param = get_param_macro(TEST_CASE, DEBUG, PING_SEND, 0, 0); if (disabled_core == IPCMEM_NUM_HOSTS) pr_err("All cores are enabled. No Disabled cores\n"); /* Pinging one enabled and disabled cores to get the error and dbg prints */ if (disabled_core < IPCMEM_NUM_HOSTS) ipclite_test_msg_send(disabled_core, param); param = get_param_macro(TEST_CASE, PING, 0, IPCLITE_TEST_STOP, 0); ipclite_test_msg_send(IPCMEM_APPS, param); wait_event_interruptible_timeout(core_wq, cores_completed == data->test_params.num_senders, msecs_to_jiffies(1000)); complete(&test_done); } return 0; } static void ipclite_test_set_enabled_cores(void) { if (data->test_params.enabled_cores < 0 || data->test_params.enabled_cores > IPCLITE_TEST_ALL_CORES) { pr_err("Invalid parameter value given to enabled cores\n"); data->test_params.enabled_cores = IPCLITE_TEST_ALL_CORES; return; } pr_info("Enabled cores set to %d\n", data->test_params.enabled_cores); } static void ipclite_test_set_wait(void) { uint64_t param; if (data->test_params.wait < 0) { pr_err("Invalid parameter value given to wait\n"); data->test_params.wait = 1; return; } pr_info("wait set to %d\n", data->test_params.wait); param = get_param_macro(WAIT, 0, data->test_params.wait, 0, 0); ping_all_enabled_cores(param); } static void ipclite_test_set_num_pings(void) { uint64_t param; pr_info("num_pings set to %d\n", data->test_params.num_pings); param = get_param_macro(NUM_PINGS, 0, data->test_params.num_pings, 0, 0); ping_all_enabled_cores(param); } static void ipclite_test_set_num_itr(void) { uint64_t param; pr_info("num_itr set to %d\n", data->test_params.num_itr); param = get_param_macro(NUM_ITR, 1, data->test_params.num_itr, 0, 0); ping_all_enabled_cores(param); } static void ipclite_test_set_receivers(void) { uint64_t param; if (data->test_params.selected_receivers < 0 || data->test_params.selected_receivers > IPCLITE_TEST_ALL_CORES) { pr_err("Invalid parameter value given to selected_receivers\n"); data->test_params.selected_receivers = 1; data->test_params.num_receivers = 1; return; } /* Check number of 1s using hamming weight function. * Number of 1s is number of receivers */ data->test_params.num_receivers = hweight_long(data->test_params.selected_receivers); pr_info("selected_receivers set to %d\n", data->test_params.selected_receivers); param = get_param_macro(RECEIVER_LIST, 0, data->test_params.selected_receivers, 0, 0); ping_all_enabled_cores(param); } static void ipclite_test_set_senders(void) { if (data->test_params.selected_senders < 0 || data->test_params.selected_senders > IPCLITE_TEST_ALL_CORES) { pr_err("Invalid parameter value given to selected_senders\n"); data->test_params.selected_senders = 1; data->test_params.num_senders = 1; return; } /* Check number of 1s using hamming weight function. */ data->test_params.num_senders = hweight_long(data->test_params.selected_senders); pr_info("selected_senders set to %d\n", data->test_params.selected_senders); } static void ipclite_test_set_num_threads(void) { uint64_t param; if (data->test_params.num_thread < 0 || data->test_params.num_thread > IPCLITE_TEST_MAX_THREADS) { pr_err("Invalid parameter value given to num_thread\n"); data->test_params.num_thread = 1; return; } pr_info("num_thread set to %d\n", data->test_params.num_thread); param = get_param_macro(NUM_THREADS, 0, data->test_params.num_thread, 0, 0); ping_all_enabled_cores(param); } static void ipclite_test_set_test(void) { uint64_t param; int ret = 0; if (data->test_params.selected_test_case < 0 || data->test_params.selected_test_case > 8) { pr_err("Invalid parameter value given to test_case\n"); data->test_params.selected_test_case = 0; return; } pr_info("selected_test_case set to %d\n", data->test_params.selected_test_case); param = get_param_macro(TEST_CASE, data->test_params.selected_test_case, 0, IPCLITE_TEST_START, 0); switch (data->test_params.selected_test_case) { case PING: ret = thread_init(&thread_data, data, wrapper_ping_test); if (ret != 0) { pr_err("Could not create thread for testing\n"); return; } thread_data.run = true; wake_up_interruptible(&thread_data.wq); break; case NEGATIVE: ping_sel_senders(param); if (is_selected_sender(IPCMEM_APPS)) { pr_info("Starting test %d for core %s\n", NEGATIVE, core_name[IPCMEM_APPS]); ret = thread_init(&thread_data, data, negative_tests); if (ret != 0) { pr_err("Could not create thread for testing\n"); return; } thread_data.run = true; wake_up_interruptible(&thread_data.wq); } break; case GLOBAL_ATOMIC: ret = thread_init(&thread_data, data, global_atomics_test_wrapper); if (ret != 0) { pr_err("Could not create thread for testing\n"); return; } thread_data.run = true; wake_up_interruptible(&thread_data.wq); break; case DEBUG: ping_sel_senders(param); if (is_selected_sender(IPCMEM_APPS)) { ret = thread_init(&thread_data, data, debug_tests); if (ret != 0) { pr_err("Could not create thread for testing\n"); return; } thread_data.run = true; wake_up_interruptible(&thread_data.wq); } break; case SSR: if (data->test_params.num_senders != 1) { pr_err("SSR Testing requires only 1 core to be selected\n"); return; } /* Find first set (ffs) to get the bit position/index of sender */ data->ssr_client = ffs(data->test_params.selected_senders) - 1; if (data->ssr_client == 0 || !is_enabled_core(data->ssr_client)) { pr_err("Invalid core selected for SSR Testing\n"); return; } pr_info("Starting test %d for core %s\n", SSR, core_name[data->ssr_client]); ret = thread_init(&thread_data, data, ssr_test); if (ret != 0) { pr_err("Could not create thread for testing\n"); return; } thread_data.run = true; wake_up_interruptible(&thread_data.wq); break; case HW_MUTEX: if (data->test_params.num_senders != 1) { pr_err("HW Mutex Testing requires only 1 core to be selected\n"); return; } if (is_selected_sender(IPCMEM_APPS)) { pr_info("Starting test %d for core %s\n", HW_MUTEX, core_name[IPCMEM_APPS]); ret = thread_init(&thread_data, data, hw_mutex_test); if (ret != 0) { pr_err("Could not create thread for testing\n"); return; } thread_data.run = true; wake_up_interruptible(&thread_data.wq); } else ping_sel_senders(param); break; default: pr_err("Wrong input provided\n"); return; } wait_for_completion(&test_done); if (thread_data.thread != NULL) ret = kthread_stop(thread_data.thread); if (ret != 0) pr_err("Test did not complete successfully\n"); else pr_info("Test completed successfully\n"); } static int parse_param(char **temp_buf, int *addr) { char *token; int ret; token = strsep(temp_buf, " "); if (!token) { pr_err("Token value is NULL in parse param\n"); return -EINVAL; } ret = kstrtoint(token, 0, addr); if (ret < 0) { pr_err("Parameter value not read correctly\n"); return ret; } return 0; } static ssize_t ipclite_test_params_write(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { char *temp_buf = kmalloc(strlen(buf)+1, GFP_KERNEL); char *temp_ptr = temp_buf; int ret, param = 0; if (!temp_buf) { pr_err("Memory not allocated\n"); return -EINVAL; } ret = strscpy(temp_buf, buf, strlen(buf)+1); if (ret < 0) { pr_err("User input is too large\n"); goto exit; } ret = parse_param(&temp_buf, ¶m); if (ret != 0) goto exit; if (param == ENABLED_CORES) { ret = parse_param(&temp_buf, &data->test_params.enabled_cores); if (ret == 0) ipclite_test_set_enabled_cores(); goto exit; } else data->test_params.selected_test_case = param; switch (data->test_params.selected_test_case) { case PING: ret = parse_param(&temp_buf, &data->test_params.selected_senders); if (ret != 0) break; ipclite_test_set_senders(); ret = parse_param(&temp_buf, &data->test_params.selected_receivers); if (ret != 0) break; ipclite_test_set_receivers(); ret = parse_param(&temp_buf, &data->test_params.num_pings); if (ret != 0) break; ipclite_test_set_num_pings(); ret = parse_param(&temp_buf, &data->test_params.wait); if (ret != 0) break; ipclite_test_set_wait(); ret = parse_param(&temp_buf, &data->test_params.num_itr); if (ret != 0) break; ipclite_test_set_num_itr(); ret = parse_param(&temp_buf, &data->test_params.num_thread); if (ret != 0) break; ipclite_test_set_num_threads(); break; case NEGATIVE: ret = parse_param(&temp_buf, &data->test_params.selected_senders); if (ret != 0) break; ipclite_test_set_senders(); ret = parse_param(&temp_buf, &data->test_params.selected_receivers); if (ret != 0) break; ipclite_test_set_receivers(); break; case GLOBAL_ATOMIC: ret = parse_param(&temp_buf, &data->test_params.selected_receivers); if (ret != 0) break; ipclite_test_set_receivers(); ret = parse_param(&temp_buf, &data->test_params.num_itr); if (ret != 0) break; ipclite_test_set_num_itr(); break; case DEBUG: ret = parse_param(&temp_buf, &data->test_params.selected_senders); if (ret != 0) break; ipclite_test_set_senders(); break; case SSR: ret = parse_param(&temp_buf, &data->test_params.selected_senders); if (ret != 0) break; ipclite_test_set_senders(); ret = parse_param(&temp_buf, &data->test_params.selected_receivers); if (ret != 0) break; ipclite_test_set_receivers(); ret = parse_param(&temp_buf, &data->test_params.num_pings); if (ret != 0) break; ipclite_test_set_num_pings(); break; case HW_MUTEX: ret = parse_param(&temp_buf, &data->test_params.selected_senders); if (ret != 0) break; ipclite_test_set_senders(); ret = parse_param(&temp_buf, &data->test_params.selected_receivers); if (ret != 0) break; ipclite_test_set_receivers(); break; default: pr_err("Wrong input provided\n"); goto exit; } if (ret == 0) ipclite_test_set_test(); exit: kfree(temp_ptr); return count; } static int ipclite_test_callback_fn(unsigned int client_id, long long msg, void *data_ptr) { struct ipclite_test_data *data = data_ptr; uint64_t header, parameter_info, test_info, payload_info, start_stop_info, pass_fail_info; uint64_t reply_macro; int ret = 0; /* Unpack the different bit fields from message value */ header = (msg & GENMASK(63, 56))>>56; parameter_info = (msg & GENMASK(55, 48))>>48; test_info = (msg & GENMASK(47, 40))>>40; payload_info = (msg & GENMASK(39, 16))>>16; start_stop_info = (msg & GENMASK(15, 8))>>8; pass_fail_info = (msg & GENMASK(7, 0)); if (!data) { pr_err("Callback data pointer not loaded successfully\n"); return -EFAULT; } data->client_id = client_id; if (header != IPCLITE_TEST_HEADER) { pr_err("Corrupted message packed received\n"); return -EINVAL; } pr_debug("The message received is %lx\n", msg); switch (test_info) { case PING: case NEGATIVE: case DEBUG: if (payload_info == PING_SEND) { reply_macro = get_param_macro(TEST_CASE, test_info, PING_REPLY, 0, 0); ipclite_test_msg_send(client_id, reply_macro); break; } if (payload_info == PING_REPLY) { ping_receive(data); break; } if (pass_fail_info == IPCLITE_TEST_PASS) pr_info("Test passed on core %s\n", core_name[client_id]); else if (pass_fail_info == IPCLITE_TEST_FAIL) pr_info("Test failed on core %s\n", core_name[client_id]); if (start_stop_info == IPCLITE_TEST_STOP) { ++cores_completed; if (cores_completed == data->test_params.num_senders) pr_info("Test completed on all cores\n"); if (is_selected_sender(IPCMEM_APPS)) wake_up_interruptible(&core_wq); else complete(&test_done); } break; case HW_MUTEX: if (start_stop_info == IPCLITE_TEST_START) { ret = ipclite_hw_mutex_release(); if (ret == 0) *((int *)data->global_memory->virt_base) = IPCMEM_APPS; reply_macro = get_param_macro(TEST_CASE, test_info, HW_MUTEX_RELEASE, IPCLITE_TEST_STOP, 0); ipclite_test_msg_send(client_id, reply_macro); } if (pass_fail_info == IPCLITE_TEST_PASS) pr_info("HW Unlock Test passed on core %s\n", core_name[client_id]); else if (pass_fail_info == IPCLITE_TEST_FAIL) pr_info("HW Unlock Test failed on core %s\n", core_name[client_id]); if (start_stop_info == IPCLITE_TEST_STOP) complete(&test_done); break; case SSR: if (payload_info == PING_SEND) { reply_macro = get_param_macro(TEST_CASE, test_info, PING_REPLY, 0, 0); data->pings_received[client_id]++; ipclite_test_msg_send(client_id, reply_macro); if (data->pings_received[client_id] == data->test_params.num_pings) { pr_info("Waking up ssr_wakeup_check_thread.\n"); pr_info("Signaling other cores to make sure there is no other crash\n"); wakeup_check.run = true; wake_up_interruptible(&wakeup_check.wq); bg_pings.run = true; wake_up_interruptible(&bg_pings.wq); } } if (payload_info == SSR_WAKEUP) { if (start_stop_info == IPCLITE_TEST_STOP) { ssr_complete = true; pr_info("%s wakeup completed\n", core_name[client_id]); wake_up_interruptible(&ssr_wq); } } if (pass_fail_info == IPCLITE_TEST_PASS) pr_info("Test %d passed on core %s\n", test_info, core_name[client_id]); else if (pass_fail_info == IPCLITE_TEST_FAIL) pr_info("Test %d failed on core %s\n", test_info, core_name[client_id]); break; case GLOBAL_ATOMIC: if (start_stop_info == IPCLITE_TEST_STOP) { pr_debug("%s completed Global Atomics Test.\n", core_name[client_id]); if (payload_info == GLOBAL_ATOMICS_SET_CLR) threads_completed++; else threads_completed += 2; wake_up_interruptible(&thread_wq); } break; default: pr_info("Wrong input given\n"); } return 0; } struct kobj_attribute ipclite_test_params = __ATTR(ipclite_test_params, 0660, NULL, ipclite_test_params_write); static int ipclite_test_sysfs_node_setup(void) { int ret = 0; sysfs_dir = kobject_create_and_add("ipclite_test", kernel_kobj); if (sysfs_dir == NULL) { pr_err("Cannot create sysfs directory\n"); return -ENOENT; } ret = sysfs_create_file(sysfs_dir, &ipclite_test_params.attr); if (ret) { pr_err("Cannot create sysfs file for ipclite test module. Error - %d\n", ret); return -ENOENT; } return 0; } static int __init ipclite_test_init(void) { int ret = 0; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->global_memory = kzalloc(sizeof(*(data->global_memory)), GFP_KERNEL); if (!data->global_memory) { kfree(data); data = NULL; return -ENOMEM; } ret = get_global_partition_info(data->global_memory); if (ret != 0) { pr_err("Unable to load global partition information\n"); goto bail; } ret = ipclite_register_test_client(ipclite_test_callback_fn, data); if (ret != 0) { pr_err("Could not register client\n"); goto bail; } ret = ipclite_test_sysfs_node_setup(); if (ret != 0) { pr_err("Failed to create sysfs interface\n"); goto bail; } init_test_params(); return 0; bail: kfree(data->global_memory); kfree(data); data = NULL; return ret; } static void __exit ipclite_test_exit(void) { pr_info("Removing IPCLite Test Module\n"); sysfs_remove_file(sysfs_dir, &ipclite_test_params.attr); kobject_put(sysfs_dir); kfree(data->global_memory); kfree(data); data = NULL; } module_init(ipclite_test_init); module_exit(ipclite_test_exit); MODULE_LICENSE("GPL v2");