xref: /linux/tools/testing/selftests/bpf/progs/task_local_data.bpf.h (revision 4f38da1f027ea2c9f01bb71daa7a299c191b6940)
1*31e838e1SAmery Hung /* SPDX-License-Identifier: GPL-2.0 */
2*31e838e1SAmery Hung #ifndef __TASK_LOCAL_DATA_BPF_H
3*31e838e1SAmery Hung #define __TASK_LOCAL_DATA_BPF_H
4*31e838e1SAmery Hung 
5*31e838e1SAmery Hung /*
6*31e838e1SAmery Hung  * Task local data is a library that facilitates sharing per-task data
7*31e838e1SAmery Hung  * between user space and bpf programs.
8*31e838e1SAmery Hung  *
9*31e838e1SAmery Hung  *
10*31e838e1SAmery Hung  * USAGE
11*31e838e1SAmery Hung  *
12*31e838e1SAmery Hung  * A TLD, an entry of data in task local data, first needs to be created by the
13*31e838e1SAmery Hung  * user space. This is done by calling user space API, TLD_DEFINE_KEY() or
14*31e838e1SAmery Hung  * tld_create_key(), with the name of the TLD and the size.
15*31e838e1SAmery Hung  *
16*31e838e1SAmery Hung  * TLD_DEFINE_KEY(prio, "priority", sizeof(int));
17*31e838e1SAmery Hung  *
18*31e838e1SAmery Hung  * or
19*31e838e1SAmery Hung  *
20*31e838e1SAmery Hung  * void func_call(...) {
21*31e838e1SAmery Hung  *     tld_key_t prio, in_cs;
22*31e838e1SAmery Hung  *
23*31e838e1SAmery Hung  *     prio = tld_create_key("priority", sizeof(int));
24*31e838e1SAmery Hung  *     in_cs = tld_create_key("in_critical_section", sizeof(bool));
25*31e838e1SAmery Hung  *     ...
26*31e838e1SAmery Hung  *
27*31e838e1SAmery Hung  * A key associated with the TLD, which has an opaque type tld_key_t, will be
28*31e838e1SAmery Hung  * initialized or returned. It can be used to get a pointer to the TLD in the
29*31e838e1SAmery Hung  * user space by calling tld_get_data().
30*31e838e1SAmery Hung  *
31*31e838e1SAmery Hung  * In a bpf program, tld_object_init() first needs to be called to initialized a
32*31e838e1SAmery Hung  * tld_object on the stack. Then, TLDs can be accessed by calling tld_get_data().
33*31e838e1SAmery Hung  * The API will try to fetch the key by the name and use it to locate the data.
34*31e838e1SAmery Hung  * A pointer to the TLD will be returned. It also caches the key in a task local
35*31e838e1SAmery Hung  * storage map, tld_key_map, whose value type, struct tld_keys, must be defined
36*31e838e1SAmery Hung  * by the developer.
37*31e838e1SAmery Hung  *
38*31e838e1SAmery Hung  * struct tld_keys {
39*31e838e1SAmery Hung  *     tld_key_t prio;
40*31e838e1SAmery Hung  *     tld_key_t in_cs;
41*31e838e1SAmery Hung  * };
42*31e838e1SAmery Hung  *
43*31e838e1SAmery Hung  * SEC("struct_ops")
44*31e838e1SAmery Hung  * void prog(struct task_struct task, ...)
45*31e838e1SAmery Hung  * {
46*31e838e1SAmery Hung  *     struct tld_object tld_obj;
47*31e838e1SAmery Hung  *     int err, *p;
48*31e838e1SAmery Hung  *
49*31e838e1SAmery Hung  *     err = tld_object_init(task, &tld_obj);
50*31e838e1SAmery Hung  *     if (err)
51*31e838e1SAmery Hung  *         return;
52*31e838e1SAmery Hung  *
53*31e838e1SAmery Hung  *     p = tld_get_data(&tld_obj, prio, "priority", sizeof(int));
54*31e838e1SAmery Hung  *     if (p)
55*31e838e1SAmery Hung  *         // do something depending on *p
56*31e838e1SAmery Hung  */
57*31e838e1SAmery Hung #include <errno.h>
58*31e838e1SAmery Hung #include <bpf/bpf_helpers.h>
59*31e838e1SAmery Hung 
60*31e838e1SAmery Hung #define TLD_ROUND_MASK(x, y) ((__typeof__(x))((y) - 1))
61*31e838e1SAmery Hung #define TLD_ROUND_UP(x, y) ((((x) - 1) | TLD_ROUND_MASK(x, y)) + 1)
62*31e838e1SAmery Hung 
63*31e838e1SAmery Hung #define TLD_MAX_DATA_CNT (__PAGE_SIZE / sizeof(struct tld_metadata) - 1)
64*31e838e1SAmery Hung 
65*31e838e1SAmery Hung #ifndef TLD_NAME_LEN
66*31e838e1SAmery Hung #define TLD_NAME_LEN 62
67*31e838e1SAmery Hung #endif
68*31e838e1SAmery Hung 
69*31e838e1SAmery Hung #ifndef TLD_KEY_MAP_CREATE_RETRY
70*31e838e1SAmery Hung #define TLD_KEY_MAP_CREATE_RETRY 10
71*31e838e1SAmery Hung #endif
72*31e838e1SAmery Hung 
73*31e838e1SAmery Hung typedef struct {
74*31e838e1SAmery Hung 	__s16 off;
75*31e838e1SAmery Hung } tld_key_t;
76*31e838e1SAmery Hung 
77*31e838e1SAmery Hung struct tld_metadata {
78*31e838e1SAmery Hung 	char name[TLD_NAME_LEN];
79*31e838e1SAmery Hung 	__u16 size;
80*31e838e1SAmery Hung };
81*31e838e1SAmery Hung 
82*31e838e1SAmery Hung struct tld_meta_u {
83*31e838e1SAmery Hung 	__u8 cnt;
84*31e838e1SAmery Hung 	__u16 size;
85*31e838e1SAmery Hung 	struct tld_metadata metadata[TLD_MAX_DATA_CNT];
86*31e838e1SAmery Hung };
87*31e838e1SAmery Hung 
88*31e838e1SAmery Hung struct tld_data_u {
89*31e838e1SAmery Hung 	__u64 start; /* offset of tld_data_u->data in a page */
90*31e838e1SAmery Hung 	char data[__PAGE_SIZE - sizeof(__u64)];
91*31e838e1SAmery Hung };
92*31e838e1SAmery Hung 
93*31e838e1SAmery Hung struct tld_map_value {
94*31e838e1SAmery Hung 	struct tld_data_u __uptr *data;
95*31e838e1SAmery Hung 	struct tld_meta_u __uptr *meta;
96*31e838e1SAmery Hung };
97*31e838e1SAmery Hung 
98*31e838e1SAmery Hung typedef struct tld_uptr_dummy {
99*31e838e1SAmery Hung 	struct tld_data_u data[0];
100*31e838e1SAmery Hung 	struct tld_meta_u meta[0];
101*31e838e1SAmery Hung } *tld_uptr_dummy_t;
102*31e838e1SAmery Hung 
103*31e838e1SAmery Hung struct tld_object {
104*31e838e1SAmery Hung 	struct tld_map_value *data_map;
105*31e838e1SAmery Hung 	struct tld_keys *key_map;
106*31e838e1SAmery Hung 	/*
107*31e838e1SAmery Hung 	 * Force the compiler to generate the actual definition of tld_meta_u
108*31e838e1SAmery Hung 	 * and tld_data_u in BTF. Without it, tld_meta_u and u_tld_data will
109*31e838e1SAmery Hung 	 * be BTF_KIND_FWD.
110*31e838e1SAmery Hung 	 */
111*31e838e1SAmery Hung 	tld_uptr_dummy_t dummy[0];
112*31e838e1SAmery Hung };
113*31e838e1SAmery Hung 
114*31e838e1SAmery Hung /*
115*31e838e1SAmery Hung  * Map value of tld_key_map for caching keys. Must be defined by the developer.
116*31e838e1SAmery Hung  * Members should be tld_key_t and passed to the 3rd argument of tld_fetch_key().
117*31e838e1SAmery Hung  */
118*31e838e1SAmery Hung struct tld_keys;
119*31e838e1SAmery Hung 
120*31e838e1SAmery Hung struct {
121*31e838e1SAmery Hung 	__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
122*31e838e1SAmery Hung 	__uint(map_flags, BPF_F_NO_PREALLOC);
123*31e838e1SAmery Hung 	__type(key, int);
124*31e838e1SAmery Hung 	__type(value, struct tld_map_value);
125*31e838e1SAmery Hung } tld_data_map SEC(".maps");
126*31e838e1SAmery Hung 
127*31e838e1SAmery Hung struct {
128*31e838e1SAmery Hung 	__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
129*31e838e1SAmery Hung 	__uint(map_flags, BPF_F_NO_PREALLOC);
130*31e838e1SAmery Hung 	__type(key, int);
131*31e838e1SAmery Hung 	__type(value, struct tld_keys);
132*31e838e1SAmery Hung } tld_key_map SEC(".maps");
133*31e838e1SAmery Hung 
134*31e838e1SAmery Hung /**
135*31e838e1SAmery Hung  * tld_object_init() - Initialize a tld_object.
136*31e838e1SAmery Hung  *
137*31e838e1SAmery Hung  * @task: The task_struct of the target task
138*31e838e1SAmery Hung  * @tld_obj: A pointer to a tld_object to be initialized
139*31e838e1SAmery Hung  *
140*31e838e1SAmery Hung  * Return 0 on success; -ENODATA if the user space did not initialize task local data
141*31e838e1SAmery Hung  * for the current task through tld_get_data(); -ENOMEM if the creation of tld_key_map
142*31e838e1SAmery Hung  * fails
143*31e838e1SAmery Hung  */
144*31e838e1SAmery Hung __attribute__((unused))
145*31e838e1SAmery Hung static int tld_object_init(struct task_struct *task, struct tld_object *tld_obj)
146*31e838e1SAmery Hung {
147*31e838e1SAmery Hung 	int i;
148*31e838e1SAmery Hung 
149*31e838e1SAmery Hung 	tld_obj->data_map = bpf_task_storage_get(&tld_data_map, task, 0, 0);
150*31e838e1SAmery Hung 	if (!tld_obj->data_map)
151*31e838e1SAmery Hung 		return -ENODATA;
152*31e838e1SAmery Hung 
153*31e838e1SAmery Hung 	bpf_for(i, 0, TLD_KEY_MAP_CREATE_RETRY) {
154*31e838e1SAmery Hung 		tld_obj->key_map = bpf_task_storage_get(&tld_key_map, task, 0,
155*31e838e1SAmery Hung 							BPF_LOCAL_STORAGE_GET_F_CREATE);
156*31e838e1SAmery Hung 		if (likely(tld_obj->key_map))
157*31e838e1SAmery Hung 			break;
158*31e838e1SAmery Hung 	}
159*31e838e1SAmery Hung 	if (!tld_obj->key_map)
160*31e838e1SAmery Hung 		return -ENOMEM;
161*31e838e1SAmery Hung 
162*31e838e1SAmery Hung 	return 0;
163*31e838e1SAmery Hung }
164*31e838e1SAmery Hung 
165*31e838e1SAmery Hung /*
166*31e838e1SAmery Hung  * Return the offset of TLD if @name is found. Otherwise, return the current TLD count
167*31e838e1SAmery Hung  * using the nonpositive range so that the next tld_get_data() can skip fetching key if
168*31e838e1SAmery Hung  * no new TLD is added or start comparing name from the first newly added TLD.
169*31e838e1SAmery Hung  */
170*31e838e1SAmery Hung __attribute__((unused))
171*31e838e1SAmery Hung static int __tld_fetch_key(struct tld_object *tld_obj, const char *name, int i_start)
172*31e838e1SAmery Hung {
173*31e838e1SAmery Hung 	struct tld_metadata *metadata;
174*31e838e1SAmery Hung 	int i, cnt, start, off = 0;
175*31e838e1SAmery Hung 
176*31e838e1SAmery Hung 	if (!tld_obj->data_map || !tld_obj->data_map->data || !tld_obj->data_map->meta)
177*31e838e1SAmery Hung 		return 0;
178*31e838e1SAmery Hung 
179*31e838e1SAmery Hung 	start = tld_obj->data_map->data->start;
180*31e838e1SAmery Hung 	cnt = tld_obj->data_map->meta->cnt;
181*31e838e1SAmery Hung 	metadata = tld_obj->data_map->meta->metadata;
182*31e838e1SAmery Hung 
183*31e838e1SAmery Hung 	bpf_for(i, 0, cnt) {
184*31e838e1SAmery Hung 		if (i >= TLD_MAX_DATA_CNT)
185*31e838e1SAmery Hung 			break;
186*31e838e1SAmery Hung 
187*31e838e1SAmery Hung 		if (i >= i_start && !bpf_strncmp(metadata[i].name, TLD_NAME_LEN, name))
188*31e838e1SAmery Hung 			return start + off;
189*31e838e1SAmery Hung 
190*31e838e1SAmery Hung 		off += TLD_ROUND_UP(metadata[i].size, 8);
191*31e838e1SAmery Hung 	}
192*31e838e1SAmery Hung 
193*31e838e1SAmery Hung 	return -cnt;
194*31e838e1SAmery Hung }
195*31e838e1SAmery Hung 
196*31e838e1SAmery Hung /**
197*31e838e1SAmery Hung  * tld_get_data() - Retrieve a pointer to the TLD associated with the name.
198*31e838e1SAmery Hung  *
199*31e838e1SAmery Hung  * @tld_obj: A pointer to a valid tld_object initialized by tld_object_init()
200*31e838e1SAmery Hung  * @key: The cached key of the TLD in tld_key_map
201*31e838e1SAmery Hung  * @name: The name of the key associated with a TLD
202*31e838e1SAmery Hung  * @size: The size of the TLD. Must be a known constant value
203*31e838e1SAmery Hung  *
204*31e838e1SAmery Hung  * Return a pointer to the TLD associated with @name; NULL if not found or @size is too
205*31e838e1SAmery Hung  * big. @key is used to cache the key if the TLD is found to speed up subsequent calls.
206*31e838e1SAmery Hung  * It should be defined as an member of tld_keys of tld_key_t type by the developer.
207*31e838e1SAmery Hung  */
208*31e838e1SAmery Hung #define tld_get_data(tld_obj, key, name, size)						\
209*31e838e1SAmery Hung 	({										\
210*31e838e1SAmery Hung 		void *data = NULL, *_data = (tld_obj)->data_map->data;			\
211*31e838e1SAmery Hung 		long off = (tld_obj)->key_map->key.off;					\
212*31e838e1SAmery Hung 		int cnt;								\
213*31e838e1SAmery Hung 											\
214*31e838e1SAmery Hung 		if (likely(_data)) {							\
215*31e838e1SAmery Hung 			if (likely(off > 0)) {						\
216*31e838e1SAmery Hung 				barrier_var(off);					\
217*31e838e1SAmery Hung 				if (likely(off < __PAGE_SIZE - size))			\
218*31e838e1SAmery Hung 					data = _data + off;				\
219*31e838e1SAmery Hung 			} else {							\
220*31e838e1SAmery Hung 				cnt = -(off);						\
221*31e838e1SAmery Hung 				if (likely((tld_obj)->data_map->meta) &&		\
222*31e838e1SAmery Hung 				    cnt < (tld_obj)->data_map->meta->cnt) {		\
223*31e838e1SAmery Hung 					off = __tld_fetch_key(tld_obj, name, cnt);	\
224*31e838e1SAmery Hung 					(tld_obj)->key_map->key.off = off;		\
225*31e838e1SAmery Hung 											\
226*31e838e1SAmery Hung 					if (likely(off < __PAGE_SIZE - size)) {		\
227*31e838e1SAmery Hung 						barrier_var(off);			\
228*31e838e1SAmery Hung 						if (off > 0)				\
229*31e838e1SAmery Hung 							data = _data + off;		\
230*31e838e1SAmery Hung 					}						\
231*31e838e1SAmery Hung 				}							\
232*31e838e1SAmery Hung 			}								\
233*31e838e1SAmery Hung 		}									\
234*31e838e1SAmery Hung 		data;									\
235*31e838e1SAmery Hung 	})
236*31e838e1SAmery Hung 
237*31e838e1SAmery Hung #endif
238