summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/timer.c
blob: 34f9ccce260293755980bcd6fcece491964f7929 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2021 Facebook */
#include <test_progs.h>
#include "timer.skel.h"
#include "timer_failure.skel.h"
#include "timer_interrupt.skel.h"

#define NUM_THR 8

static void *spin_lock_thread(void *arg)
{
	int i, err, prog_fd = *(int *)arg;
	LIBBPF_OPTS(bpf_test_run_opts, topts);

	for (i = 0; i < 10000; i++) {
		err = bpf_prog_test_run_opts(prog_fd, &topts);
		if (!ASSERT_OK(err, "test_run_opts err") ||
		    !ASSERT_OK(topts.retval, "test_run_opts retval"))
			break;
	}

	pthread_exit(arg);
}

static int timer(struct timer *timer_skel)
{
	int i, err, prog_fd;
	LIBBPF_OPTS(bpf_test_run_opts, topts);
	pthread_t thread_id[NUM_THR];
	void *ret;

	err = timer__attach(timer_skel);
	if (!ASSERT_OK(err, "timer_attach"))
		return err;

	ASSERT_EQ(timer_skel->data->callback_check, 52, "callback_check1");
	ASSERT_EQ(timer_skel->data->callback2_check, 52, "callback2_check1");
	ASSERT_EQ(timer_skel->bss->pinned_callback_check, 0, "pinned_callback_check1");

	prog_fd = bpf_program__fd(timer_skel->progs.test1);
	err = bpf_prog_test_run_opts(prog_fd, &topts);
	ASSERT_OK(err, "test_run");
	ASSERT_EQ(topts.retval, 0, "test_run");
	timer__detach(timer_skel);

	usleep(50); /* 10 usecs should be enough, but give it extra */
	/* check that timer_cb1() was executed 10+10 times */
	ASSERT_EQ(timer_skel->data->callback_check, 42, "callback_check2");
	ASSERT_EQ(timer_skel->data->callback2_check, 42, "callback2_check2");

	/* check that timer_cb2() was executed twice */
	ASSERT_EQ(timer_skel->bss->bss_data, 10, "bss_data");

	/* check that timer_cb3() was executed twice */
	ASSERT_EQ(timer_skel->bss->abs_data, 12, "abs_data");

	/* check that timer_cb_pinned() was executed twice */
	ASSERT_EQ(timer_skel->bss->pinned_callback_check, 2, "pinned_callback_check");

	/* check that there were no errors in timer execution */
	ASSERT_EQ(timer_skel->bss->err, 0, "err");

	/* check that code paths completed */
	ASSERT_EQ(timer_skel->bss->ok, 1 | 2 | 4, "ok");

	prog_fd = bpf_program__fd(timer_skel->progs.race);
	for (i = 0; i < NUM_THR; i++) {
		err = pthread_create(&thread_id[i], NULL,
				     &spin_lock_thread, &prog_fd);
		if (!ASSERT_OK(err, "pthread_create"))
			break;
	}

	while (i) {
		err = pthread_join(thread_id[--i], &ret);
		if (ASSERT_OK(err, "pthread_join"))
			ASSERT_EQ(ret, (void *)&prog_fd, "pthread_join");
	}

	return 0;
}

/* TODO: use pid filtering */
void serial_test_timer(void)
{
	struct timer *timer_skel = NULL;
	int err;

	timer_skel = timer__open_and_load();
	if (!timer_skel && errno == EOPNOTSUPP) {
		test__skip();
		return;
	}
	if (!ASSERT_OK_PTR(timer_skel, "timer_skel_load"))
		return;

	err = timer(timer_skel);
	ASSERT_OK(err, "timer");
	timer__destroy(timer_skel);

	RUN_TESTS(timer_failure);
}

void test_timer_interrupt(void)
{
	struct timer_interrupt *skel = NULL;
	int err, prog_fd;
	LIBBPF_OPTS(bpf_test_run_opts, opts);

	skel = timer_interrupt__open_and_load();
	if (!skel && errno == EOPNOTSUPP) {
		test__skip();
		return;
	}
	if (!ASSERT_OK_PTR(skel, "timer_interrupt__open_and_load"))
		return;

	err = timer_interrupt__attach(skel);
	if (!ASSERT_OK(err, "timer_interrupt__attach"))
		goto out;

	prog_fd = bpf_program__fd(skel->progs.test_timer_interrupt);
	err = bpf_prog_test_run_opts(prog_fd, &opts);
	if (!ASSERT_OK(err, "bpf_prog_test_run_opts"))
		goto out;

	usleep(50);

	ASSERT_EQ(skel->bss->in_interrupt, 0, "in_interrupt");
	if (skel->bss->preempt_count)
		ASSERT_NEQ(skel->bss->in_interrupt_cb, 0, "in_interrupt_cb");

out:
	timer_interrupt__destroy(skel);
}