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
19struct lookup_ctx {
20	bool start;
21	bool stop;
22	int pid_fd;
23	int map_fd;
24	int loop;
25};
26
27static 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
41static 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
51void 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);
122out:
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