xref: /linux/tools/testing/selftests/mm/page_frag/page_frag_test.c (revision 60675d4ca1ef0857e44eba5849b74a3a998d0c0f)
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