File Index
Files and directories
Sign in
FILE

printk_ringbuffer_kunit_test.c

File preview with syntax highlighting.

linux-6.19.14/kernel/printk/printk_ringbuffer_kunit_test.c C
  1// SPDX-License-Identifier: GPL-2.0
  2
  3#include <linux/cpuhplock.h>
  4#include <linux/cpumask.h>
  5#include <linux/init.h>
  6#include <linux/kthread.h>
  7#include <linux/module.h>
  8#include <linux/moduleparam.h>
  9#include <linux/random.h>
 10#include <linux/slab.h>
 11#include <linux/timer.h>
 12#include <linux/wait.h>
 13
 14#include <kunit/resource.h>
 15#include <kunit/test.h>
 16
 17#include "printk_ringbuffer.h"
 18
 19/*
 20 * This KUnit tests the data integrity of the lockless printk_ringbuffer.
 21 * From multiple CPUs it writes messages of varying length and content while
 22 * a reader validates the correctness of the messages.
 23 *
 24 * IMPORTANT: The more CPUs you can use for this KUnit, the better!
 25 *
 26 * The test works by starting "num_online_cpus() - 1" writer threads, each
 27 * pinned to their own CPU. Each writer thread loops, writing data of varying
 28 * length into a printk_ringbuffer as fast as possible. The data content is
 29 * an embedded data struct followed by string content repeating the byte:
 30 *
 31 *      'A' + CPUID
 32 *
 33 * The reader is running on the remaining online CPU, or if there is only one
 34 * CPU on the same as the writer.
 35 * It ensures that the embedded struct content is consistent with the string
 36 * and that the string * is terminated and is composed of the same repeating
 37 * byte as its first byte.
 38 *
 39 * Because the threads are running in such tight loops, they will call
 40 * cond_resched() from time to time so the system stays functional.
 41 *
 42 * If the reader encounters an error, the test is aborted and some
 43 * information about the error is reported.
 44 * The runtime of the test can be configured with the runtime_ms module parameter.
 45 *
 46 * Note that the test is performed on a separate printk_ringbuffer instance
 47 * and not the instance used by printk().
 48 */
 49
 50static unsigned long runtime_ms = 10 * MSEC_PER_SEC;
 51module_param(runtime_ms, ulong, 0400);
 52
 53/* test data structure */
 54struct prbtest_rbdata {
 55	unsigned int size;
 56	char text[] __counted_by(size);
 57};
 58
 59#define MAX_RBDATA_TEXT_SIZE 0x80
 60#define MAX_PRB_RECORD_SIZE (sizeof(struct prbtest_rbdata) + MAX_RBDATA_TEXT_SIZE)
 61
 62struct prbtest_data {
 63	struct kunit *test;
 64	struct printk_ringbuffer *ringbuffer;
 65	/* used by writers to signal reader of new records */
 66	wait_queue_head_t new_record_wait;
 67};
 68
 69struct prbtest_thread_data {
 70	unsigned long num;
 71	struct prbtest_data *test_data;
 72};
 73
 74static void prbtest_fail_record(struct kunit *test, const struct prbtest_rbdata *dat, u64 seq)
 75{
 76	unsigned int len;
 77
 78	len = dat->size - 1;
 79
 80	KUNIT_FAIL(test, "BAD RECORD: seq=%llu size=%u text=%.*s\n",
 81		   seq, dat->size,
 82		   len < MAX_RBDATA_TEXT_SIZE ? len : -1,
 83		   len < MAX_RBDATA_TEXT_SIZE ? dat->text : "<invalid>");
 84}
 85
 86static bool prbtest_check_data(const struct prbtest_rbdata *dat)
 87{
 88	unsigned int len;
 89
 90	/* Sane size? At least one character + trailing '\0' */
 91	if (dat->size < 2 || dat->size > MAX_RBDATA_TEXT_SIZE)
 92		return false;
 93
 94	len = dat->size - 1;
 95	if (dat->text[len] != '\0')
 96		return false;
 97
 98	/* String repeats with the same character? */
 99	while (len--) {
100		if (dat->text[len] != dat->text[0])
101			return false;
102	}
103
104	return true;
105}
106
107static int prbtest_writer(void *data)
108{
109	struct prbtest_thread_data *tr = data;
110	char text_id = 'A' + tr->num;
111	struct prb_reserved_entry e;
112	struct prbtest_rbdata *dat;
113	u32 record_size, text_size;
114	unsigned long count = 0;
115	struct printk_record r;
116
117	kunit_info(tr->test_data->test, "start thread %03lu (writer)\n", tr->num);
118
119	for (;;) {
120		/* ensure at least 1 character + trailing '\0' */
121		text_size = get_random_u32_inclusive(2, MAX_RBDATA_TEXT_SIZE);
122		if (WARN_ON_ONCE(text_size < 2))
123			text_size = 2;
124		if (WARN_ON_ONCE(text_size > MAX_RBDATA_TEXT_SIZE))
125			text_size = MAX_RBDATA_TEXT_SIZE;
126
127		record_size = sizeof(struct prbtest_rbdata) + text_size;
128		WARN_ON_ONCE(record_size > MAX_PRB_RECORD_SIZE);
129
130		/* specify the text sizes for reservation */
131		prb_rec_init_wr(&r, record_size);
132
133		/*
134		 * Reservation can fail if:
135		 *
136		 *      - No free descriptor is available.
137		 *      - The buffer is full, and the oldest record is reserved
138		 *        but not yet committed.
139		 *
140		 * It actually happens in this test because all CPUs are trying
141		 * to write an unbounded number of messages in a tight loop.
142		 * These failures are intentionally ignored because this test
143		 * focuses on races, ringbuffer consistency, and pushing system
144		 * usability limits.
145		 */
146		if (prb_reserve(&e, tr->test_data->ringbuffer, &r)) {
147			r.info->text_len = record_size;
148
149			dat = (struct prbtest_rbdata *)r.text_buf;
150			dat->size = text_size;
151			memset(dat->text, text_id, text_size - 1);
152			dat->text[text_size - 1] = '\0';
153
154			prb_commit(&e);
155
156			wake_up_interruptible(&tr->test_data->new_record_wait);
157		}
158
159		if ((count++ & 0x3fff) == 0)
160			cond_resched();
161
162		if (kthread_should_stop())
163			break;
164	}
165
166	kunit_info(tr->test_data->test, "end thread %03lu: wrote=%lu\n", tr->num, count);
167
168	return 0;
169}
170
171struct prbtest_wakeup_timer {
172	struct timer_list timer;
173	struct task_struct *task;
174};
175
176static void prbtest_wakeup_callback(struct timer_list *timer)
177{
178	struct prbtest_wakeup_timer *wakeup = timer_container_of(wakeup, timer, timer);
179
180	set_tsk_thread_flag(wakeup->task, TIF_NOTIFY_SIGNAL);
181	wake_up_process(wakeup->task);
182}
183
184static int prbtest_reader(struct prbtest_data *test_data, unsigned long timeout_ms)
185{
186	struct prbtest_wakeup_timer wakeup;
187	char text_buf[MAX_PRB_RECORD_SIZE];
188	unsigned long count = 0;
189	struct printk_info info;
190	struct printk_record r;
191	u64 seq = 0;
192
193	wakeup.task = current;
194	timer_setup_on_stack(&wakeup.timer, prbtest_wakeup_callback, 0);
195	mod_timer(&wakeup.timer, jiffies + msecs_to_jiffies(timeout_ms));
196
197	prb_rec_init_rd(&r, &info, text_buf, sizeof(text_buf));
198
199	kunit_info(test_data->test, "start reader\n");
200
201	while (!wait_event_interruptible(test_data->new_record_wait,
202					 prb_read_valid(test_data->ringbuffer, seq, &r))) {
203		/* check/track the sequence */
204		if (info.seq < seq)
205			KUNIT_FAIL(test_data->test, "BAD SEQ READ: request=%llu read=%llu\n",
206				   seq, info.seq);
207
208		if (!prbtest_check_data((struct prbtest_rbdata *)r.text_buf))
209			prbtest_fail_record(test_data->test,
210					    (struct prbtest_rbdata *)r.text_buf, info.seq);
211
212		if ((count++ & 0x3fff) == 0)
213			cond_resched();
214
215		seq = info.seq + 1;
216	}
217
218	timer_delete_sync(&wakeup.timer);
219	timer_destroy_on_stack(&wakeup.timer);
220
221	kunit_info(test_data->test, "end reader: read=%lu seq=%llu\n", count, info.seq);
222
223	return 0;
224}
225
226KUNIT_DEFINE_ACTION_WRAPPER(prbtest_cpumask_cleanup, free_cpumask_var, struct cpumask *);
227KUNIT_DEFINE_ACTION_WRAPPER(prbtest_kthread_cleanup, kthread_stop, struct task_struct *);
228
229static void prbtest_add_cpumask_cleanup(struct kunit *test, cpumask_var_t mask)
230{
231	int err;
232
233	err = kunit_add_action_or_reset(test, prbtest_cpumask_cleanup, mask);
234	KUNIT_ASSERT_EQ(test, err, 0);
235}
236
237static void prbtest_add_kthread_cleanup(struct kunit *test, struct task_struct *kthread)
238{
239	int err;
240
241	err = kunit_add_action_or_reset(test, prbtest_kthread_cleanup, kthread);
242	KUNIT_ASSERT_EQ(test, err, 0);
243}
244
245static inline void prbtest_prb_reinit(struct printk_ringbuffer *rb)
246{
247	prb_init(rb, rb->text_data_ring.data, rb->text_data_ring.size_bits, rb->desc_ring.descs,
248		 rb->desc_ring.count_bits, rb->desc_ring.infos);
249}
250
251static void test_readerwriter(struct kunit *test)
252{
253	/* Equivalent to CONFIG_LOG_BUF_SHIFT=13 */
254	DEFINE_PRINTKRB(test_rb, 8, 5);
255
256	struct prbtest_thread_data *thread_data;
257	struct prbtest_data *test_data;
258	struct task_struct *thread;
259	cpumask_var_t test_cpus;
260	int cpu, reader_cpu;
261
262	KUNIT_ASSERT_TRUE(test, alloc_cpumask_var(&test_cpus, GFP_KERNEL));
263	prbtest_add_cpumask_cleanup(test, test_cpus);
264
265	cpus_read_lock();
266	/*
267	 * Failure of KUNIT_ASSERT() kills the current task
268	 * so it can not be called while the CPU hotplug lock is held.
269	 * Instead use a snapshot of the online CPUs.
270	 * If they change during test execution it is unfortunate but not a grave error.
271	 */
272	cpumask_copy(test_cpus, cpu_online_mask);
273	cpus_read_unlock();
274
275	/* One CPU is for the reader, all others are writers */
276	reader_cpu = cpumask_first(test_cpus);
277	if (cpumask_weight(test_cpus) == 1)
278		kunit_warn(test, "more than one CPU is recommended");
279	else
280		cpumask_clear_cpu(reader_cpu, test_cpus);
281
282	/* KUnit test can get restarted more times. */
283	prbtest_prb_reinit(&test_rb);
284
285	test_data = kunit_kmalloc(test, sizeof(*test_data), GFP_KERNEL);
286	KUNIT_ASSERT_NOT_NULL(test, test_data);
287	test_data->test = test;
288	test_data->ringbuffer = &test_rb;
289	init_waitqueue_head(&test_data->new_record_wait);
290
291	kunit_info(test, "running for %lu ms\n", runtime_ms);
292
293	for_each_cpu(cpu, test_cpus) {
294		thread_data = kunit_kmalloc(test, sizeof(*thread_data), GFP_KERNEL);
295		KUNIT_ASSERT_NOT_NULL(test, thread_data);
296		thread_data->test_data = test_data;
297		thread_data->num = cpu;
298
299		thread = kthread_run_on_cpu(prbtest_writer, thread_data, cpu,
300					    "prbtest writer %u");
301		KUNIT_ASSERT_NOT_ERR_OR_NULL(test, thread);
302		prbtest_add_kthread_cleanup(test, thread);
303	}
304
305	kunit_info(test, "starting test\n");
306
307	set_cpus_allowed_ptr(current, cpumask_of(reader_cpu));
308	prbtest_reader(test_data, runtime_ms);
309
310	kunit_info(test, "completed test\n");
311}
312
313static struct kunit_case prb_test_cases[] = {
314	KUNIT_CASE_SLOW(test_readerwriter),
315	{}
316};
317
318static struct kunit_suite prb_test_suite = {
319	.name       = "printk-ringbuffer",
320	.test_cases = prb_test_cases,
321};
322kunit_test_suite(prb_test_suite);
323
324MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
325MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
326MODULE_DESCRIPTION("printk_ringbuffer KUnit test");
327MODULE_LICENSE("GPL");
~/ Read-only file preview.