File Index
Files and directories
Sign in
FILE

bench_page_pool_simple.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/net/bench/page_pool/bench_page_pool_simple.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Benchmark module for page_pool.
  4 *
  5 */
  6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  7
  8#include <linux/interrupt.h>
  9#include <linux/limits.h>
 10#include <linux/module.h>
 11#include <linux/mutex.h>
 12#include <net/page_pool/helpers.h>
 13
 14#include "time_bench.h"
 15
 16static int verbose = 1;
 17#define MY_POOL_SIZE 1024
 18
 19/* Makes tests selectable. Useful for perf-record to analyze a single test.
 20 * Hint: Bash shells support writing binary number like: $((2#101010)
 21 *
 22 * # modprobe bench_page_pool_simple run_flags=$((2#100))
 23 */
 24static unsigned long run_flags = 0xFFFFFFFF;
 25module_param(run_flags, ulong, 0);
 26MODULE_PARM_DESC(run_flags, "Limit which bench test that runs");
 27
 28/* Count the bit number from the enum */
 29enum benchmark_bit {
 30	bit_run_bench_baseline,
 31	bit_run_bench_no_softirq01,
 32	bit_run_bench_no_softirq02,
 33	bit_run_bench_no_softirq03,
 34};
 35
 36#define bit(b)		(1 << (b))
 37#define enabled(b)	((run_flags & (bit(b))))
 38
 39/* notice time_bench is limited to U32_MAX nr loops */
 40static unsigned long loops = 10000000;
 41module_param(loops, ulong, 0);
 42MODULE_PARM_DESC(loops, "Specify loops bench will run");
 43
 44/* Timing at the nanosec level, we need to know the overhead
 45 * introduced by the for loop itself
 46 */
 47static int time_bench_for_loop(struct time_bench_record *rec, void *data)
 48{
 49	uint64_t loops_cnt = 0;
 50	int i;
 51
 52	time_bench_start(rec);
 53	/** Loop to measure **/
 54	for (i = 0; i < rec->loops; i++) {
 55		loops_cnt++;
 56		barrier(); /* avoid compiler to optimize this loop */
 57	}
 58	time_bench_stop(rec, loops_cnt);
 59	return loops_cnt;
 60}
 61
 62static int time_bench_atomic_inc(struct time_bench_record *rec, void *data)
 63{
 64	uint64_t loops_cnt = 0;
 65	atomic_t cnt;
 66	int i;
 67
 68	atomic_set(&cnt, 0);
 69
 70	time_bench_start(rec);
 71	/** Loop to measure **/
 72	for (i = 0; i < rec->loops; i++) {
 73		atomic_inc(&cnt);
 74		barrier(); /* avoid compiler to optimize this loop */
 75	}
 76	loops_cnt = atomic_read(&cnt);
 77	time_bench_stop(rec, loops_cnt);
 78	return loops_cnt;
 79}
 80
 81/* The ptr_ping in page_pool uses a spinlock. We need to know the minimum
 82 * overhead of taking+releasing a spinlock, to know the cycles that can be saved
 83 * by e.g. amortizing this via bulking.
 84 */
 85static int time_bench_lock(struct time_bench_record *rec, void *data)
 86{
 87	uint64_t loops_cnt = 0;
 88	spinlock_t lock;
 89	int i;
 90
 91	spin_lock_init(&lock);
 92
 93	time_bench_start(rec);
 94	/** Loop to measure **/
 95	for (i = 0; i < rec->loops; i++) {
 96		spin_lock(&lock);
 97		loops_cnt++;
 98		barrier(); /* avoid compiler to optimize this loop */
 99		spin_unlock(&lock);
100	}
101	time_bench_stop(rec, loops_cnt);
102	return loops_cnt;
103}
104
105/* Helper for filling some page's into ptr_ring */
106static void pp_fill_ptr_ring(struct page_pool *pp, int elems)
107{
108	/* GFP_ATOMIC needed when under run softirq */
109	gfp_t gfp_mask = GFP_ATOMIC;
110	struct page **array;
111	int i;
112
113	array = kcalloc(elems, sizeof(struct page *), gfp_mask);
114
115	for (i = 0; i < elems; i++)
116		array[i] = page_pool_alloc_pages(pp, gfp_mask);
117	for (i = 0; i < elems; i++)
118		page_pool_put_page(pp, array[i], -1, false);
119
120	kfree(array);
121}
122
123enum test_type { type_fast_path, type_ptr_ring, type_page_allocator };
124
125/* Depends on compile optimizing this function */
126static int time_bench_page_pool(struct time_bench_record *rec, void *data,
127				enum test_type type, const char *func)
128{
129	uint64_t loops_cnt = 0;
130	gfp_t gfp_mask = GFP_ATOMIC; /* GFP_ATOMIC is not really needed */
131	int i, err;
132
133	struct page_pool *pp;
134	struct page *page;
135
136	struct page_pool_params pp_params = {
137		.order = 0,
138		.flags = 0,
139		.pool_size = MY_POOL_SIZE,
140		.nid = NUMA_NO_NODE,
141		.dev = NULL, /* Only use for DMA mapping */
142		.dma_dir = DMA_BIDIRECTIONAL,
143	};
144
145	pp = page_pool_create(&pp_params);
146	if (IS_ERR(pp)) {
147		err = PTR_ERR(pp);
148		pr_warn("%s: Error(%d) creating page_pool\n", func, err);
149		goto out;
150	}
151	pp_fill_ptr_ring(pp, 64);
152
153	if (in_serving_softirq())
154		pr_warn("%s(): in_serving_softirq fast-path\n", func);
155	else
156		pr_warn("%s(): Cannot use page_pool fast-path\n", func);
157
158	time_bench_start(rec);
159	/** Loop to measure **/
160	for (i = 0; i < rec->loops; i++) {
161		/* Common fast-path alloc that depend on in_serving_softirq() */
162		page = page_pool_alloc_pages(pp, gfp_mask);
163		if (!page)
164			break;
165		loops_cnt++;
166		barrier(); /* avoid compiler to optimize this loop */
167
168		/* The benchmarks purpose it to test different return paths.
169		 * Compiler should inline optimize other function calls out
170		 */
171		if (type == type_fast_path) {
172			/* Fast-path recycling e.g. XDP_DROP use-case */
173			page_pool_recycle_direct(pp, page);
174
175		} else if (type == type_ptr_ring) {
176			/* Normal return path */
177			page_pool_put_page(pp, page, -1, false);
178
179		} else if (type == type_page_allocator) {
180			/* Test if not pages are recycled, but instead
181			 * returned back into systems page allocator
182			 */
183			get_page(page); /* cause no-recycling */
184			page_pool_put_page(pp, page, -1, false);
185			put_page(page);
186		} else {
187			BUILD_BUG();
188		}
189	}
190	time_bench_stop(rec, loops_cnt);
191out:
192	page_pool_destroy(pp);
193	return loops_cnt;
194}
195
196static int time_bench_page_pool01_fast_path(struct time_bench_record *rec,
197					    void *data)
198{
199	return time_bench_page_pool(rec, data, type_fast_path, __func__);
200}
201
202static int time_bench_page_pool02_ptr_ring(struct time_bench_record *rec,
203					   void *data)
204{
205	return time_bench_page_pool(rec, data, type_ptr_ring, __func__);
206}
207
208static int time_bench_page_pool03_slow(struct time_bench_record *rec,
209				       void *data)
210{
211	return time_bench_page_pool(rec, data, type_page_allocator, __func__);
212}
213
214static int run_benchmark_tests(void)
215{
216	uint32_t nr_loops = loops;
217
218	/* Baseline tests */
219	if (enabled(bit_run_bench_baseline)) {
220		time_bench_loop(nr_loops * 10, 0, "for_loop", NULL,
221				time_bench_for_loop);
222		time_bench_loop(nr_loops * 10, 0, "atomic_inc", NULL,
223				time_bench_atomic_inc);
224		time_bench_loop(nr_loops, 0, "lock", NULL, time_bench_lock);
225	}
226
227	/* This test cannot activate correct code path, due to no-softirq ctx */
228	if (enabled(bit_run_bench_no_softirq01))
229		time_bench_loop(nr_loops, 0, "no-softirq-page_pool01", NULL,
230				time_bench_page_pool01_fast_path);
231	if (enabled(bit_run_bench_no_softirq02))
232		time_bench_loop(nr_loops, 0, "no-softirq-page_pool02", NULL,
233				time_bench_page_pool02_ptr_ring);
234	if (enabled(bit_run_bench_no_softirq03))
235		time_bench_loop(nr_loops, 0, "no-softirq-page_pool03", NULL,
236				time_bench_page_pool03_slow);
237
238	return 0;
239}
240
241static int __init bench_page_pool_simple_module_init(void)
242{
243	if (verbose)
244		pr_info("Loaded\n");
245
246	if (loops > U32_MAX) {
247		pr_err("Module param loops(%lu) exceeded U32_MAX(%u)\n", loops,
248		       U32_MAX);
249		return -ECHRNG;
250	}
251
252	run_benchmark_tests();
253
254	return 0;
255}
256module_init(bench_page_pool_simple_module_init);
257
258static void __exit bench_page_pool_simple_module_exit(void)
259{
260	if (verbose)
261		pr_info("Unloaded\n");
262}
263module_exit(bench_page_pool_simple_module_exit);
264
265MODULE_DESCRIPTION("Benchmark of page_pool simple cases");
266MODULE_AUTHOR("Jesper Dangaard Brouer <netoptimizer@brouer.com>");
267MODULE_LICENSE("GPL");
~/ Read-only file preview.