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