1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2022. Huawei Technologies Co., Ltd */
3 #define _GNU_SOURCE
4 #include <sched.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include <stdbool.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <pthread.h>
11 
12 #include <bpf/bpf.h>
13 #include <bpf/libbpf.h>
14 
15 #include "test_maps.h"
16 #include "task_local_storage_helpers.h"
17 #include "read_bpf_task_storage_busy.skel.h"
18 
19 struct lookup_ctx {
20 	bool start;
21 	bool stop;
22 	int pid_fd;
23 	int map_fd;
24 	int loop;
25 };
26 
lookup_fn(void * arg)27 static void *lookup_fn(void *arg)
28 {
29 	struct lookup_ctx *ctx = arg;
30 	long value;
31 	int i = 0;
32 
33 	while (!ctx->start)
34 		usleep(1);
35 
36 	while (!ctx->stop && i++ < ctx->loop)
37 		bpf_map_lookup_elem(ctx->map_fd, &ctx->pid_fd, &value);
38 	return NULL;
39 }
40 
abort_lookup(struct lookup_ctx * ctx,pthread_t * tids,unsigned int nr)41 static void abort_lookup(struct lookup_ctx *ctx, pthread_t *tids, unsigned int nr)
42 {
43 	unsigned int i;
44 
45 	ctx->stop = true;
46 	ctx->start = true;
47 	for (i = 0; i < nr; i++)
48 		pthread_join(tids[i], NULL);
49 }
50 
test_task_storage_map_stress_lookup(void)51 void test_task_storage_map_stress_lookup(void)
52 {
53 #define MAX_NR_THREAD 4096
54 	unsigned int i, nr = 256, loop = 8192, cpu = 0;
55 	struct read_bpf_task_storage_busy *skel;
56 	pthread_t tids[MAX_NR_THREAD];
57 	struct lookup_ctx ctx;
58 	cpu_set_t old, new;
59 	const char *cfg;
60 	int err;
61 
62 	cfg = getenv("TASK_STORAGE_MAP_NR_THREAD");
63 	if (cfg) {
64 		nr = atoi(cfg);
65 		if (nr > MAX_NR_THREAD)
66 			nr = MAX_NR_THREAD;
67 	}
68 	cfg = getenv("TASK_STORAGE_MAP_NR_LOOP");
69 	if (cfg)
70 		loop = atoi(cfg);
71 	cfg = getenv("TASK_STORAGE_MAP_PIN_CPU");
72 	if (cfg)
73 		cpu = atoi(cfg);
74 
75 	skel = read_bpf_task_storage_busy__open_and_load();
76 	err = libbpf_get_error(skel);
77 	CHECK(err, "open_and_load", "error %d\n", err);
78 
79 	/* Only for a fully preemptible kernel */
80 	if (!skel->kconfig->CONFIG_PREEMPT) {
81 		printf("%s SKIP (no CONFIG_PREEMPT)\n", __func__);
82 		read_bpf_task_storage_busy__destroy(skel);
83 		skips++;
84 		return;
85 	}
86 
87 	/* Save the old affinity setting */
88 	sched_getaffinity(getpid(), sizeof(old), &old);
89 
90 	/* Pinned on a specific CPU */
91 	CPU_ZERO(&new);
92 	CPU_SET(cpu, &new);
93 	sched_setaffinity(getpid(), sizeof(new), &new);
94 
95 	ctx.start = false;
96 	ctx.stop = false;
97 	ctx.pid_fd = sys_pidfd_open(getpid(), 0);
98 	ctx.map_fd = bpf_map__fd(skel->maps.task);
99 	ctx.loop = loop;
100 	for (i = 0; i < nr; i++) {
101 		err = pthread_create(&tids[i], NULL, lookup_fn, &ctx);
102 		if (err) {
103 			abort_lookup(&ctx, tids, i);
104 			CHECK(err, "pthread_create", "error %d\n", err);
105 			goto out;
106 		}
107 	}
108 
109 	ctx.start = true;
110 	for (i = 0; i < nr; i++)
111 		pthread_join(tids[i], NULL);
112 
113 	skel->bss->pid = getpid();
114 	err = read_bpf_task_storage_busy__attach(skel);
115 	CHECK(err, "attach", "error %d\n", err);
116 
117 	/* Trigger program */
118 	syscall(SYS_gettid);
119 	skel->bss->pid = 0;
120 
121 	CHECK(skel->bss->busy != 0, "bad bpf_task_storage_busy", "got %d\n", skel->bss->busy);
122 out:
123 	read_bpf_task_storage_busy__destroy(skel);
124 	/* Restore affinity setting */
125 	sched_setaffinity(getpid(), sizeof(old), &old);
126 	printf("%s:PASS\n", __func__);
127 }
128