17fef0decSYunsheng Lin // SPDX-License-Identifier: GPL-2.0 27fef0decSYunsheng Lin 37fef0decSYunsheng Lin /* 47fef0decSYunsheng Lin * Test module for page_frag cache 57fef0decSYunsheng Lin * 67fef0decSYunsheng Lin * Copyright (C) 2024 Yunsheng Lin <linyunsheng@huawei.com> 77fef0decSYunsheng Lin */ 87fef0decSYunsheng Lin 97fef0decSYunsheng Lin #include <linux/module.h> 107fef0decSYunsheng Lin #include <linux/cpumask.h> 117fef0decSYunsheng Lin #include <linux/completion.h> 127fef0decSYunsheng Lin #include <linux/ptr_ring.h> 137fef0decSYunsheng Lin #include <linux/kthread.h> 1465941f10SYunsheng Lin #include <linux/page_frag_cache.h> 157fef0decSYunsheng Lin 167fef0decSYunsheng Lin #define TEST_FAILED_PREFIX "page_frag_test failed: " 177fef0decSYunsheng Lin 187fef0decSYunsheng Lin static struct ptr_ring ptr_ring; 197fef0decSYunsheng Lin static int nr_objs = 512; 207fef0decSYunsheng Lin static atomic_t nthreads; 217fef0decSYunsheng Lin static struct completion wait; 227fef0decSYunsheng Lin static struct page_frag_cache test_nc; 237fef0decSYunsheng Lin static int test_popped; 247fef0decSYunsheng Lin static int test_pushed; 257fef0decSYunsheng Lin static bool force_exit; 267fef0decSYunsheng Lin 277fef0decSYunsheng Lin static int nr_test = 2000000; 287fef0decSYunsheng Lin module_param(nr_test, int, 0); 297fef0decSYunsheng Lin MODULE_PARM_DESC(nr_test, "number of iterations to test"); 307fef0decSYunsheng Lin 317fef0decSYunsheng Lin static bool test_align; 327fef0decSYunsheng Lin module_param(test_align, bool, 0); 337fef0decSYunsheng Lin MODULE_PARM_DESC(test_align, "use align API for testing"); 347fef0decSYunsheng Lin 357fef0decSYunsheng Lin static int test_alloc_len = 2048; 367fef0decSYunsheng Lin module_param(test_alloc_len, int, 0); 377fef0decSYunsheng Lin MODULE_PARM_DESC(test_alloc_len, "alloc len for testing"); 387fef0decSYunsheng Lin 397fef0decSYunsheng Lin static int test_push_cpu; 407fef0decSYunsheng Lin module_param(test_push_cpu, int, 0); 417fef0decSYunsheng Lin MODULE_PARM_DESC(test_push_cpu, "test cpu for pushing fragment"); 427fef0decSYunsheng Lin 437fef0decSYunsheng Lin static int test_pop_cpu; 447fef0decSYunsheng Lin module_param(test_pop_cpu, int, 0); 457fef0decSYunsheng Lin MODULE_PARM_DESC(test_pop_cpu, "test cpu for popping fragment"); 467fef0decSYunsheng Lin 477fef0decSYunsheng Lin static int page_frag_pop_thread(void *arg) 487fef0decSYunsheng Lin { 497fef0decSYunsheng Lin struct ptr_ring *ring = arg; 507fef0decSYunsheng Lin 517fef0decSYunsheng Lin pr_info("page_frag pop test thread begins on cpu %d\n", 527fef0decSYunsheng Lin smp_processor_id()); 537fef0decSYunsheng Lin 547fef0decSYunsheng Lin while (test_popped < nr_test) { 557fef0decSYunsheng Lin void *obj = __ptr_ring_consume(ring); 567fef0decSYunsheng Lin 577fef0decSYunsheng Lin if (obj) { 587fef0decSYunsheng Lin test_popped++; 597fef0decSYunsheng Lin page_frag_free(obj); 607fef0decSYunsheng Lin } else { 617fef0decSYunsheng Lin if (force_exit) 627fef0decSYunsheng Lin break; 637fef0decSYunsheng Lin 647fef0decSYunsheng Lin cond_resched(); 657fef0decSYunsheng Lin } 667fef0decSYunsheng Lin } 677fef0decSYunsheng Lin 687fef0decSYunsheng Lin if (atomic_dec_and_test(&nthreads)) 697fef0decSYunsheng Lin complete(&wait); 707fef0decSYunsheng Lin 717fef0decSYunsheng Lin pr_info("page_frag pop test thread exits on cpu %d\n", 727fef0decSYunsheng Lin smp_processor_id()); 737fef0decSYunsheng Lin 747fef0decSYunsheng Lin return 0; 757fef0decSYunsheng Lin } 767fef0decSYunsheng Lin 777fef0decSYunsheng Lin static int page_frag_push_thread(void *arg) 787fef0decSYunsheng Lin { 797fef0decSYunsheng Lin struct ptr_ring *ring = arg; 807fef0decSYunsheng Lin 817fef0decSYunsheng Lin pr_info("page_frag push test thread begins on cpu %d\n", 827fef0decSYunsheng Lin smp_processor_id()); 837fef0decSYunsheng Lin 847fef0decSYunsheng Lin while (test_pushed < nr_test && !force_exit) { 857fef0decSYunsheng Lin void *va; 867fef0decSYunsheng Lin int ret; 877fef0decSYunsheng Lin 887fef0decSYunsheng Lin if (test_align) { 897fef0decSYunsheng Lin va = page_frag_alloc_align(&test_nc, test_alloc_len, 907fef0decSYunsheng Lin GFP_KERNEL, SMP_CACHE_BYTES); 917fef0decSYunsheng Lin 927fef0decSYunsheng Lin if ((unsigned long)va & (SMP_CACHE_BYTES - 1)) { 937fef0decSYunsheng Lin force_exit = true; 947fef0decSYunsheng Lin WARN_ONCE(true, TEST_FAILED_PREFIX "unaligned va returned\n"); 957fef0decSYunsheng Lin } 967fef0decSYunsheng Lin } else { 977fef0decSYunsheng Lin va = page_frag_alloc(&test_nc, test_alloc_len, GFP_KERNEL); 987fef0decSYunsheng Lin } 997fef0decSYunsheng Lin 1007fef0decSYunsheng Lin if (!va) 1017fef0decSYunsheng Lin continue; 1027fef0decSYunsheng Lin 1037fef0decSYunsheng Lin ret = __ptr_ring_produce(ring, va); 1047fef0decSYunsheng Lin if (ret) { 1057fef0decSYunsheng Lin page_frag_free(va); 1067fef0decSYunsheng Lin cond_resched(); 1077fef0decSYunsheng Lin } else { 1087fef0decSYunsheng Lin test_pushed++; 1097fef0decSYunsheng Lin } 1107fef0decSYunsheng Lin } 1117fef0decSYunsheng Lin 1127fef0decSYunsheng Lin pr_info("page_frag push test thread exits on cpu %d\n", 1137fef0decSYunsheng Lin smp_processor_id()); 1147fef0decSYunsheng Lin 1157fef0decSYunsheng Lin if (atomic_dec_and_test(&nthreads)) 1167fef0decSYunsheng Lin complete(&wait); 1177fef0decSYunsheng Lin 1187fef0decSYunsheng Lin return 0; 1197fef0decSYunsheng Lin } 1207fef0decSYunsheng Lin 1217fef0decSYunsheng Lin static int __init page_frag_test_init(void) 1227fef0decSYunsheng Lin { 1237fef0decSYunsheng Lin struct task_struct *tsk_push, *tsk_pop; 1247fef0decSYunsheng Lin int last_pushed = 0, last_popped = 0; 1257fef0decSYunsheng Lin ktime_t start; 1267fef0decSYunsheng Lin u64 duration; 1277fef0decSYunsheng Lin int ret; 1287fef0decSYunsheng Lin 129*3d18dfe6SYunsheng Lin page_frag_cache_init(&test_nc); 1307fef0decSYunsheng Lin atomic_set(&nthreads, 2); 1317fef0decSYunsheng Lin init_completion(&wait); 1327fef0decSYunsheng Lin 1337fef0decSYunsheng Lin if (test_alloc_len > PAGE_SIZE || test_alloc_len <= 0 || 1347fef0decSYunsheng Lin !cpu_active(test_push_cpu) || !cpu_active(test_pop_cpu)) 1357fef0decSYunsheng Lin return -EINVAL; 1367fef0decSYunsheng Lin 1377fef0decSYunsheng Lin ret = ptr_ring_init(&ptr_ring, nr_objs, GFP_KERNEL); 1387fef0decSYunsheng Lin if (ret) 1397fef0decSYunsheng Lin return ret; 1407fef0decSYunsheng Lin 1417fef0decSYunsheng Lin tsk_push = kthread_create_on_cpu(page_frag_push_thread, &ptr_ring, 1427fef0decSYunsheng Lin test_push_cpu, "page_frag_push"); 1437fef0decSYunsheng Lin if (IS_ERR(tsk_push)) 1447fef0decSYunsheng Lin return PTR_ERR(tsk_push); 1457fef0decSYunsheng Lin 1467fef0decSYunsheng Lin tsk_pop = kthread_create_on_cpu(page_frag_pop_thread, &ptr_ring, 1477fef0decSYunsheng Lin test_pop_cpu, "page_frag_pop"); 1487fef0decSYunsheng Lin if (IS_ERR(tsk_pop)) { 1497fef0decSYunsheng Lin kthread_stop(tsk_push); 1507fef0decSYunsheng Lin return PTR_ERR(tsk_pop); 1517fef0decSYunsheng Lin } 1527fef0decSYunsheng Lin 1537fef0decSYunsheng Lin start = ktime_get(); 1547fef0decSYunsheng Lin wake_up_process(tsk_push); 1557fef0decSYunsheng Lin wake_up_process(tsk_pop); 1567fef0decSYunsheng Lin 1577fef0decSYunsheng Lin pr_info("waiting for test to complete\n"); 1587fef0decSYunsheng Lin 1597fef0decSYunsheng Lin while (!wait_for_completion_timeout(&wait, msecs_to_jiffies(10000))) { 1607fef0decSYunsheng Lin /* exit if there is no progress for push or pop size */ 1617fef0decSYunsheng Lin if (last_pushed == test_pushed || last_popped == test_popped) { 1627fef0decSYunsheng Lin WARN_ONCE(true, TEST_FAILED_PREFIX "no progress\n"); 1637fef0decSYunsheng Lin force_exit = true; 1647fef0decSYunsheng Lin continue; 1657fef0decSYunsheng Lin } 1667fef0decSYunsheng Lin 1677fef0decSYunsheng Lin last_pushed = test_pushed; 1687fef0decSYunsheng Lin last_popped = test_popped; 1697fef0decSYunsheng Lin pr_info("page_frag_test progress: pushed = %d, popped = %d\n", 1707fef0decSYunsheng Lin test_pushed, test_popped); 1717fef0decSYunsheng Lin } 1727fef0decSYunsheng Lin 1737fef0decSYunsheng Lin if (force_exit) { 1747fef0decSYunsheng Lin pr_err(TEST_FAILED_PREFIX "exit with error\n"); 1757fef0decSYunsheng Lin goto out; 1767fef0decSYunsheng Lin } 1777fef0decSYunsheng Lin 1787fef0decSYunsheng Lin duration = (u64)ktime_us_delta(ktime_get(), start); 1797fef0decSYunsheng Lin pr_info("%d of iterations for %s testing took: %lluus\n", nr_test, 1807fef0decSYunsheng Lin test_align ? "aligned" : "non-aligned", duration); 1817fef0decSYunsheng Lin 1827fef0decSYunsheng Lin out: 1837fef0decSYunsheng Lin ptr_ring_cleanup(&ptr_ring, NULL); 1847fef0decSYunsheng Lin page_frag_cache_drain(&test_nc); 1857fef0decSYunsheng Lin 1867fef0decSYunsheng Lin return -EAGAIN; 1877fef0decSYunsheng Lin } 1887fef0decSYunsheng Lin 1897fef0decSYunsheng Lin static void __exit page_frag_test_exit(void) 1907fef0decSYunsheng Lin { 1917fef0decSYunsheng Lin } 1927fef0decSYunsheng Lin 1937fef0decSYunsheng Lin module_init(page_frag_test_init); 1947fef0decSYunsheng Lin module_exit(page_frag_test_exit); 1957fef0decSYunsheng Lin 1967fef0decSYunsheng Lin MODULE_LICENSE("GPL"); 1977fef0decSYunsheng Lin MODULE_AUTHOR("Yunsheng Lin <linyunsheng@huawei.com>"); 1987fef0decSYunsheng Lin MODULE_DESCRIPTION("Test module for page_frag"); 199