To access per-task data, BPF programs usually creates a hash table with pid as the key. This is not ideal because: 1. The user need to estimate the proper size of the hash table, which may be inaccurate; 2. Big hash tables are slow; 3. To clean up the data properly during task terminations, the user need to write extra logic. Task local storage overcomes these issues and offers a better option for these per-task data. Task local storage is only available to BPF_LSM. Now enable it for tracing programs. Unlike LSM programs, tracing programs can be called in IRQ contexts. Helpers that access task local storage are updated to use raw_spin_lock_irqsave() instead of raw_spin_lock_bh(). Tracing programs can attach to functions on the task free path, e.g. exit_creds(). To avoid allocating task local storage after bpf_task_storage_free(). bpf_task_storage_get() is updated to not allocate new storage when the task is not refcounted (task->usage == 0).
Showing the changes to kernel (6 of 10 files). Show all
Unified · Split
| @@ -9,8 +9,8 @@ CFLAGS_core.o += $(call cc-disable-warning, override-init) $(cflags-nogcse-yy) · 8 unmodified lines | |||
| 9 | obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_iter.o map_iter.o task_iter.o prog_iter.o | 9 | obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_iter.o map_iter.o task_iter.o prog_iter.o |
| 10 | obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o | 10 | obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o |
| 11 | obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o | 11 | obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o |
| 12 | obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o | ||
| 12 | obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o | 13 | obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o |
| 13 | obj-${CONFIG_BPF_LSM} += bpf_task_storage.o | ||
| 14 | obj-$(CONFIG_BPF_SYSCALL) += disasm.o | 14 | obj-$(CONFIG_BPF_SYSCALL) += disasm.o |
| 15 | obj-$(CONFIG_BPF_JIT) += trampoline.o | 15 | obj-$(CONFIG_BPF_JIT) += trampoline.o |
| 16 | obj-$(CONFIG_BPF_SYSCALL) += btf.o | 16 | obj-$(CONFIG_BPF_SYSCALL) += btf.o |
| @@ -18,7 +18,6 @@ obj-$(CONFIG_BPF_JIT) += dispatcher.o · 1 unmodified line | |||
| 18 | ifeq ($(CONFIG_NET),y) | 18 | ifeq ($(CONFIG_NET),y) |
| 19 | obj-$(CONFIG_BPF_SYSCALL) += devmap.o | 19 | obj-$(CONFIG_BPF_SYSCALL) += devmap.o |
| 20 | obj-$(CONFIG_BPF_SYSCALL) += cpumap.o | 20 | obj-$(CONFIG_BPF_SYSCALL) += cpumap.o |
| 21 | obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o | ||
| 22 | obj-$(CONFIG_BPF_SYSCALL) += offload.o | 21 | obj-$(CONFIG_BPF_SYSCALL) += offload.o |
| 23 | obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o | 22 | obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o |
| 24 | endif | 23 | endif |
| @@ -140,17 +140,18 @@ static void __bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem) · 139 unmodified lines | |||
| 140 | { | 140 | { |
| 141 | struct bpf_local_storage *local_storage; | 141 | struct bpf_local_storage *local_storage; |
| 142 | bool free_local_storage = false; | 142 | bool free_local_storage = false; |
| 143 | unsigned long flags; | ||
| 143 | 144 | ||
| 144 | if (unlikely(!selem_linked_to_storage(selem))) | 145 | if (unlikely(!selem_linked_to_storage(selem))) |
| 145 | /* selem has already been unlinked from sk */ | 146 | /* selem has already been unlinked from sk */ |
| 146 | return; | 147 | return; |
| 147 | 148 | ||
| 148 | local_storage = rcu_dereference(selem->local_storage); | 149 | local_storage = rcu_dereference(selem->local_storage); |
| 149 | raw_spin_lock_bh(&local_storage->lock); | 150 | raw_spin_lock_irqsave(&local_storage->lock, flags); |
| 150 | if (likely(selem_linked_to_storage(selem))) | 151 | if (likely(selem_linked_to_storage(selem))) |
| 151 | free_local_storage = bpf_selem_unlink_storage_nolock( | 152 | free_local_storage = bpf_selem_unlink_storage_nolock( |
| 152 | local_storage, selem, true); | 153 | local_storage, selem, true); |
| 153 | raw_spin_unlock_bh(&local_storage->lock); | 154 | raw_spin_unlock_irqrestore(&local_storage->lock, flags); |
| 154 | 155 | ||
| 155 | if (free_local_storage) | 156 | if (free_local_storage) |
| 156 | kfree_rcu(local_storage, rcu); | 157 | kfree_rcu(local_storage, rcu); |
| @@ -167,6 +168,7 @@ void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem) · 10 unmodified lines | |||
| 167 | { | 168 | { |
| 168 | struct bpf_local_storage_map *smap; | 169 | struct bpf_local_storage_map *smap; |
| 169 | struct bpf_local_storage_map_bucket *b; | 170 | struct bpf_local_storage_map_bucket *b; |
| 171 | unsigned long flags; | ||
| 170 | 172 | ||
| 171 | if (unlikely(!selem_linked_to_map(selem))) | 173 | if (unlikely(!selem_linked_to_map(selem))) |
| 172 | /* selem has already be unlinked from smap */ | 174 | /* selem has already be unlinked from smap */ |
| @@ -174,21 +176,22 @@ void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem) · 1 unmodified line | |||
| 174 | 176 | ||
| 175 | smap = rcu_dereference(SDATA(selem)->smap); | 177 | smap = rcu_dereference(SDATA(selem)->smap); |
| 176 | b = select_bucket(smap, selem); | 178 | b = select_bucket(smap, selem); |
| 177 | raw_spin_lock_bh(&b->lock); | 179 | raw_spin_lock_irqsave(&b->lock, flags); |
| 178 | if (likely(selem_linked_to_map(selem))) | 180 | if (likely(selem_linked_to_map(selem))) |
| 179 | hlist_del_init_rcu(&selem->map_node); | 181 | hlist_del_init_rcu(&selem->map_node); |
| 180 | raw_spin_unlock_bh(&b->lock); | 182 | raw_spin_unlock_irqrestore(&b->lock, flags); |
| 181 | } | 183 | } |
| 182 | 184 | ||
| 183 | void bpf_selem_link_map(struct bpf_local_storage_map *smap, | 185 | void bpf_selem_link_map(struct bpf_local_storage_map *smap, |
| 184 | struct bpf_local_storage_elem *selem) | 186 | struct bpf_local_storage_elem *selem) |
| 185 | { | 187 | { |
| 186 | struct bpf_local_storage_map_bucket *b = select_bucket(smap, selem); | 188 | struct bpf_local_storage_map_bucket *b = select_bucket(smap, selem); |
| 189 | unsigned long flags; | ||
| 187 | 190 | ||
| 188 | raw_spin_lock_bh(&b->lock); | 191 | raw_spin_lock_irqsave(&b->lock, flags); |
| 189 | RCU_INIT_POINTER(SDATA(selem)->smap, smap); | 192 | RCU_INIT_POINTER(SDATA(selem)->smap, smap); |
| 190 | hlist_add_head_rcu(&selem->map_node, &b->list); | 193 | hlist_add_head_rcu(&selem->map_node, &b->list); |
| 191 | raw_spin_unlock_bh(&b->lock); | 194 | raw_spin_unlock_irqrestore(&b->lock, flags); |
| 192 | } | 195 | } |
| 193 | 196 | ||
| 194 | void bpf_selem_unlink(struct bpf_local_storage_elem *selem) | 197 | void bpf_selem_unlink(struct bpf_local_storage_elem *selem) |
| @@ -224,16 +227,18 @@ bpf_local_storage_lookup(struct bpf_local_storage *local_storage, · 29 unmodified lines | |||
| 224 | 227 | ||
| 225 | sdata = SDATA(selem); | 228 | sdata = SDATA(selem); |
| 226 | if (cacheit_lockit) { | 229 | if (cacheit_lockit) { |
| 230 | unsigned long flags; | ||
| 231 | |||
| 227 | /* spinlock is needed to avoid racing with the | 232 | /* spinlock is needed to avoid racing with the |
| 228 | * parallel delete. Otherwise, publishing an already | 233 | * parallel delete. Otherwise, publishing an already |
| 229 | * deleted sdata to the cache will become a use-after-free | 234 | * deleted sdata to the cache will become a use-after-free |
| 230 | * problem in the next bpf_local_storage_lookup(). | 235 | * problem in the next bpf_local_storage_lookup(). |
| 231 | */ | 236 | */ |
| 232 | raw_spin_lock_bh(&local_storage->lock); | 237 | raw_spin_lock_irqsave(&local_storage->lock, flags); |
| 233 | if (selem_linked_to_storage(selem)) | 238 | if (selem_linked_to_storage(selem)) |
| 234 | rcu_assign_pointer(local_storage->cache[smap->cache_idx], | 239 | rcu_assign_pointer(local_storage->cache[smap->cache_idx], |
| 235 | sdata); | 240 | sdata); |
| 236 | raw_spin_unlock_bh(&local_storage->lock); | 241 | raw_spin_unlock_irqrestore(&local_storage->lock, flags); |
| 237 | } | 242 | } |
| 238 | 243 | ||
| 239 | return sdata; | 244 | return sdata; |
| @@ -327,6 +332,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, · 87 unmodified lines | |||
| 327 | struct bpf_local_storage_data *old_sdata = NULL; | 332 | struct bpf_local_storage_data *old_sdata = NULL; |
| 328 | struct bpf_local_storage_elem *selem; | 333 | struct bpf_local_storage_elem *selem; |
| 329 | struct bpf_local_storage *local_storage; | 334 | struct bpf_local_storage *local_storage; |
| 335 | unsigned long flags; | ||
| 330 | int err; | 336 | int err; |
| 331 | 337 | ||
| 332 | /* BPF_EXIST and BPF_NOEXIST cannot be both set */ | 338 | /* BPF_EXIST and BPF_NOEXIST cannot be both set */ |
| @@ -374,7 +380,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, · 41 unmodified lines | |||
| 374 | } | 380 | } |
| 375 | } | 381 | } |
| 376 | 382 | ||
| 377 | raw_spin_lock_bh(&local_storage->lock); | 383 | raw_spin_lock_irqsave(&local_storage->lock, flags); |
| 378 | 384 | ||
| 379 | /* Recheck local_storage->list under local_storage->lock */ | 385 | /* Recheck local_storage->list under local_storage->lock */ |
| 380 | if (unlikely(hlist_empty(&local_storage->list))) { | 386 | if (unlikely(hlist_empty(&local_storage->list))) { |
| @@ -428,11 +434,11 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, · 47 unmodified lines | |||
| 428 | } | 434 | } |
| 429 | 435 | ||
| 430 | unlock: | 436 | unlock: |
| 431 | raw_spin_unlock_bh(&local_storage->lock); | 437 | raw_spin_unlock_irqrestore(&local_storage->lock, flags); |
| 432 | return SDATA(selem); | 438 | return SDATA(selem); |
| 433 | 439 | ||
| 434 | unlock_err: | 440 | unlock_err: |
| 435 | raw_spin_unlock_bh(&local_storage->lock); | 441 | raw_spin_unlock_irqrestore(&local_storage->lock, flags); |
| 436 | return ERR_PTR(err); | 442 | return ERR_PTR(err); |
| 437 | } | 443 | } |
| 438 | 444 | ||
| @@ -115,10 +115,6 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 114 unmodified lines | |||
| 115 | return &bpf_spin_lock_proto; | 115 | return &bpf_spin_lock_proto; |
| 116 | case BPF_FUNC_spin_unlock: | 116 | case BPF_FUNC_spin_unlock: |
| 117 | return &bpf_spin_unlock_proto; | 117 | return &bpf_spin_unlock_proto; |
| 118 | case BPF_FUNC_task_storage_get: | ||
| 119 | return &bpf_task_storage_get_proto; | ||
| 120 | case BPF_FUNC_task_storage_delete: | ||
| 121 | return &bpf_task_storage_delete_proto; | ||
| 122 | case BPF_FUNC_bprm_opts_set: | 118 | case BPF_FUNC_bprm_opts_set: |
| 123 | return &bpf_bprm_opts_set_proto; | 119 | return &bpf_bprm_opts_set_proto; |
| 124 | case BPF_FUNC_ima_inode_hash: | 120 | case BPF_FUNC_ima_inode_hash: |
| @@ -15,7 +15,6 @@ · 14 unmodified lines | |||
| 15 | #include <linux/bpf_local_storage.h> | 15 | #include <linux/bpf_local_storage.h> |
| 16 | #include <linux/filter.h> | 16 | #include <linux/filter.h> |
| 17 | #include <uapi/linux/btf.h> | 17 | #include <uapi/linux/btf.h> |
| 18 | #include <linux/bpf_lsm.h> | ||
| 19 | #include <linux/btf_ids.h> | 18 | #include <linux/btf_ids.h> |
| 20 | #include <linux/fdtable.h> | 19 | #include <linux/fdtable.h> |
| 21 | 20 | ||
| @@ -24,12 +23,8 @@ DEFINE_BPF_STORAGE_CACHE(task_cache); · 2 unmodified lines | |||
| 24 | static struct bpf_local_storage __rcu **task_storage_ptr(void *owner) | 23 | static struct bpf_local_storage __rcu **task_storage_ptr(void *owner) |
| 25 | { | 24 | { |
| 26 | struct task_struct *task = owner; | 25 | struct task_struct *task = owner; |
| 27 | struct bpf_storage_blob *bsb; | ||
| 28 | 26 | ||
| 29 | bsb = bpf_task(task); | 27 | return &task->bpf_storage; |
| 30 | if (!bsb) | ||
| 31 | return NULL; | ||
| 32 | return &bsb->storage; | ||
| 33 | } | 28 | } |
| 34 | 29 | ||
| 35 | static struct bpf_local_storage_data * | 30 | static struct bpf_local_storage_data * |
| @@ -38,13 +33,8 @@ task_storage_lookup(struct task_struct *task, struct bpf_map *map, · 2 unmodified lines | |||
| 38 | { | 33 | { |
| 39 | struct bpf_local_storage *task_storage; | 34 | struct bpf_local_storage *task_storage; |
| 40 | struct bpf_local_storage_map *smap; | 35 | struct bpf_local_storage_map *smap; |
| 41 | struct bpf_storage_blob *bsb; | ||
| 42 | |||
| 43 | bsb = bpf_task(task); | ||
| 44 | if (!bsb) | ||
| 45 | return NULL; | ||
| 46 | 36 | ||
| 47 | task_storage = rcu_dereference(bsb->storage); | 37 | task_storage = rcu_dereference(task->bpf_storage); |
| 48 | if (!task_storage) | 38 | if (!task_storage) |
| 49 | return NULL; | 39 | return NULL; |
| 50 | 40 | ||
| @@ -57,16 +47,12 @@ void bpf_task_storage_free(struct task_struct *task) · 6 unmodified lines | |||
| 57 | struct bpf_local_storage_elem *selem; | 47 | struct bpf_local_storage_elem *selem; |
| 58 | struct bpf_local_storage *local_storage; | 48 | struct bpf_local_storage *local_storage; |
| 59 | bool free_task_storage = false; | 49 | bool free_task_storage = false; |
| 60 | struct bpf_storage_blob *bsb; | ||
| 61 | struct hlist_node *n; | 50 | struct hlist_node *n; |
| 62 | 51 | unsigned long flags; | |
| 63 | bsb = bpf_task(task); | ||
| 64 | if (!bsb) | ||
| 65 | return; | ||
| 66 | 52 | ||
| 67 | rcu_read_lock(); | 53 | rcu_read_lock(); |
| 68 | 54 | ||
| 69 | local_storage = rcu_dereference(bsb->storage); | 55 | local_storage = rcu_dereference(task->bpf_storage); |
| 70 | if (!local_storage) { | 56 | if (!local_storage) { |
| 71 | rcu_read_unlock(); | 57 | rcu_read_unlock(); |
| 72 | return; | 58 | return; |
| @@ -81,7 +67,7 @@ void bpf_task_storage_free(struct task_struct *task) · 8 unmodified lines | |||
| 81 | * when unlinking elem from the local_storage->list and | 67 | * when unlinking elem from the local_storage->list and |
| 82 | * the map's bucket->list. | 68 | * the map's bucket->list. |
| 83 | */ | 69 | */ |
| 84 | raw_spin_lock_bh(&local_storage->lock); | 70 | raw_spin_lock_irqsave(&local_storage->lock, flags); |
| 85 | hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { | 71 | hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) { |
| 86 | /* Always unlink from map before unlinking from | 72 | /* Always unlink from map before unlinking from |
| 87 | * local_storage. | 73 | * local_storage. |
| @@ -90,7 +76,7 @@ void bpf_task_storage_free(struct task_struct *task) · 2 unmodified lines | |||
| 90 | free_task_storage = bpf_selem_unlink_storage_nolock( | 76 | free_task_storage = bpf_selem_unlink_storage_nolock( |
| 91 | local_storage, selem, false); | 77 | local_storage, selem, false); |
| 92 | } | 78 | } |
| 93 | raw_spin_unlock_bh(&local_storage->lock); | 79 | raw_spin_unlock_irqrestore(&local_storage->lock, flags); |
| 94 | rcu_read_unlock(); | 80 | rcu_read_unlock(); |
| 95 | 81 | ||
| 96 | /* free_task_storage should always be true as long as | 82 | /* free_task_storage should always be true as long as |
| @@ -150,7 +136,7 @@ static int bpf_pid_task_storage_update_elem(struct bpf_map *map, void *key, · 53 unmodified lines | |||
| 150 | */ | 136 | */ |
| 151 | WARN_ON_ONCE(!rcu_read_lock_held()); | 137 | WARN_ON_ONCE(!rcu_read_lock_held()); |
| 152 | task = pid_task(pid, PIDTYPE_PID); | 138 | task = pid_task(pid, PIDTYPE_PID); |
| 153 | if (!task || !task_storage_ptr(task)) { | 139 | if (!task) { |
| 154 | err = -ENOENT; | 140 | err = -ENOENT; |
| 155 | goto out; | 141 | goto out; |
| 156 | } | 142 | } |
| @@ -213,23 +199,16 @@ BPF_CALL_4(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *, · 56 unmodified lines | |||
| 213 | if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) | 199 | if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE)) |
| 214 | return (unsigned long)NULL; | 200 | return (unsigned long)NULL; |
| 215 | 201 | ||
| 216 | /* explicitly check that the task_storage_ptr is not | 202 | if (!task) |
| 217 | * NULL as task_storage_lookup returns NULL in this case and | ||
| 218 | * bpf_local_storage_update expects the owner to have a | ||
| 219 | * valid storage pointer. | ||
| 220 | */ | ||
| 221 | if (!task || !task_storage_ptr(task)) | ||
| 222 | return (unsigned long)NULL; | 203 | return (unsigned long)NULL; |
| 223 | 204 | ||
| 224 | sdata = task_storage_lookup(task, map, true); | 205 | sdata = task_storage_lookup(task, map, true); |
| 225 | if (sdata) | 206 | if (sdata) |
| 226 | return (unsigned long)sdata->data; | 207 | return (unsigned long)sdata->data; |
| 227 | 208 | ||
| 228 | /* This helper must only be called from places where the lifetime of the task | 209 | /* only allocate new storage, when the task is refcounted */ |
| 229 | * is guaranteed. Either by being refcounted or by being protected | 210 | if (refcount_read(&task->usage) && |
| 230 | * by an RCU read-side critical section. | 211 | (flags & BPF_LOCAL_STORAGE_GET_F_CREATE)) { |
| 231 | */ | ||
| 232 | if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) { | ||
| 233 | sdata = bpf_local_storage_update( | 212 | sdata = bpf_local_storage_update( |
| 234 | task, (struct bpf_local_storage_map *)map, value, | 213 | task, (struct bpf_local_storage_map *)map, value, |
| 235 | BPF_NOEXIST); | 214 | BPF_NOEXIST); |
| @@ -96,6 +96,7 @@ · 95 unmodified lines | |||
| 96 | #include <linux/kasan.h> | 96 | #include <linux/kasan.h> |
| 97 | #include <linux/scs.h> | 97 | #include <linux/scs.h> |
| 98 | #include <linux/io_uring.h> | 98 | #include <linux/io_uring.h> |
| 99 | #include <linux/bpf.h> | ||
| 99 | 100 | ||
| 100 | #include <asm/pgalloc.h> | 101 | #include <asm/pgalloc.h> |
| 101 | #include <linux/uaccess.h> | 102 | #include <linux/uaccess.h> |
| @@ -734,6 +735,7 @@ void __put_task_struct(struct task_struct *tsk) · 632 unmodified lines | |||
| 734 | cgroup_free(tsk); | 735 | cgroup_free(tsk); |
| 735 | task_numa_free(tsk, true); | 736 | task_numa_free(tsk, true); |
| 736 | security_task_free(tsk); | 737 | security_task_free(tsk); |
| 738 | bpf_task_storage_free(tsk); | ||
| 737 | exit_creds(tsk); | 739 | exit_creds(tsk); |
| 738 | delayacct_tsk_free(tsk); | 740 | delayacct_tsk_free(tsk); |
| 739 | put_signal_struct(tsk->signal); | 741 | put_signal_struct(tsk->signal); |
| @@ -2062,6 +2064,9 @@ static __latent_entropy struct task_struct *copy_process( · 1322 unmodified lines | |||
| 2062 | p->sequential_io = 0; | 2064 | p->sequential_io = 0; |
| 2063 | p->sequential_io_avg = 0; | 2065 | p->sequential_io_avg = 0; |
| 2064 | #endif | 2066 | #endif |
| 2067 | #ifdef CONFIG_BPF_SYSCALL | ||
| 2068 | RCU_INIT_POINTER(p->bpf_storage, NULL); | ||
| 2069 | #endif | ||
| 2065 | 2070 | ||
| 2066 | /* Perform scheduler related setup. Assign this task to a CPU. */ | 2071 | /* Perform scheduler related setup. Assign this task to a CPU. */ |
| 2067 | retval = sched_fork(clone_flags, p); | 2072 | retval = sched_fork(clone_flags, p); |
| @@ -1367,6 +1367,10 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 1366 unmodified lines | |||
| 1367 | return &bpf_per_cpu_ptr_proto; | 1367 | return &bpf_per_cpu_ptr_proto; |
| 1368 | case BPF_FUNC_this_cpu_ptr: | 1368 | case BPF_FUNC_this_cpu_ptr: |
| 1369 | return &bpf_this_cpu_ptr_proto; | 1369 | return &bpf_this_cpu_ptr_proto; |
| 1370 | case BPF_FUNC_task_storage_get: | ||
| 1371 | return &bpf_task_storage_get_proto; | ||
| 1372 | case BPF_FUNC_task_storage_delete: | ||
| 1373 | return &bpf_task_storage_delete_proto; | ||
| 1370 | default: | 1374 | default: |
| 1371 | return NULL; | 1375 | return NULL; |
| 1372 | } | 1376 | } |