1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 4 #include <vmlinux.h> 5 #include <bpf/bpf_helpers.h> 6 #include <bpf/bpf_tracing.h> 7 #include "bpf_misc.h" 8 #include "bpf_experimental.h" 9 10 char _license[] SEC("license") = "GPL"; 11 12 /* Timer tests */ 13 14 struct timer_elem { 15 struct bpf_timer t; 16 }; 17 18 struct { 19 __uint(type, BPF_MAP_TYPE_ARRAY); 20 __uint(max_entries, 1); 21 __type(key, int); 22 __type(value, struct timer_elem); 23 } timer_map SEC(".maps"); 24 25 static int timer_cb(void *map, int *key, struct bpf_timer *timer) 26 { 27 u32 data; 28 /* Timer callbacks are never sleepable, even from non-sleepable programs */ 29 bpf_copy_from_user(&data, sizeof(data), NULL); 30 return 0; 31 } 32 33 SEC("fentry/bpf_fentry_test1") 34 __failure __msg("helper call might sleep in a non-sleepable prog") 35 int timer_non_sleepable_prog(void *ctx) 36 { 37 struct timer_elem *val; 38 int key = 0; 39 40 val = bpf_map_lookup_elem(&timer_map, &key); 41 if (!val) 42 return 0; 43 44 bpf_timer_init(&val->t, &timer_map, 0); 45 bpf_timer_set_callback(&val->t, timer_cb); 46 return 0; 47 } 48 49 SEC("lsm.s/file_open") 50 __failure __msg("helper call might sleep in a non-sleepable prog") 51 int timer_sleepable_prog(void *ctx) 52 { 53 struct timer_elem *val; 54 int key = 0; 55 56 val = bpf_map_lookup_elem(&timer_map, &key); 57 if (!val) 58 return 0; 59 60 bpf_timer_init(&val->t, &timer_map, 0); 61 bpf_timer_set_callback(&val->t, timer_cb); 62 return 0; 63 } 64 65 /* Workqueue tests */ 66 67 struct wq_elem { 68 struct bpf_wq w; 69 }; 70 71 struct { 72 __uint(type, BPF_MAP_TYPE_ARRAY); 73 __uint(max_entries, 1); 74 __type(key, int); 75 __type(value, struct wq_elem); 76 } wq_map SEC(".maps"); 77 78 static int wq_cb(void *map, int *key, void *value) 79 { 80 u32 data; 81 /* Workqueue callbacks are always sleepable, even from non-sleepable programs */ 82 bpf_copy_from_user(&data, sizeof(data), NULL); 83 return 0; 84 } 85 86 SEC("fentry/bpf_fentry_test1") 87 __success 88 int wq_non_sleepable_prog(void *ctx) 89 { 90 struct wq_elem *val; 91 int key = 0; 92 93 val = bpf_map_lookup_elem(&wq_map, &key); 94 if (!val) 95 return 0; 96 97 if (bpf_wq_init(&val->w, &wq_map, 0) != 0) 98 return 0; 99 if (bpf_wq_set_callback_impl(&val->w, wq_cb, 0, NULL) != 0) 100 return 0; 101 return 0; 102 } 103 104 SEC("lsm.s/file_open") 105 __success 106 int wq_sleepable_prog(void *ctx) 107 { 108 struct wq_elem *val; 109 int key = 0; 110 111 val = bpf_map_lookup_elem(&wq_map, &key); 112 if (!val) 113 return 0; 114 115 if (bpf_wq_init(&val->w, &wq_map, 0) != 0) 116 return 0; 117 if (bpf_wq_set_callback_impl(&val->w, wq_cb, 0, NULL) != 0) 118 return 0; 119 return 0; 120 } 121 122 /* Task work tests */ 123 124 struct task_work_elem { 125 struct bpf_task_work tw; 126 }; 127 128 struct { 129 __uint(type, BPF_MAP_TYPE_ARRAY); 130 __uint(max_entries, 1); 131 __type(key, int); 132 __type(value, struct task_work_elem); 133 } task_work_map SEC(".maps"); 134 135 static int task_work_cb(struct bpf_map *map, void *key, void *value) 136 { 137 u32 data; 138 /* Task work callbacks are always sleepable, even from non-sleepable programs */ 139 bpf_copy_from_user(&data, sizeof(data), NULL); 140 return 0; 141 } 142 143 SEC("fentry/bpf_fentry_test1") 144 __success 145 int task_work_non_sleepable_prog(void *ctx) 146 { 147 struct task_work_elem *val; 148 struct task_struct *task; 149 int key = 0; 150 151 val = bpf_map_lookup_elem(&task_work_map, &key); 152 if (!val) 153 return 0; 154 155 task = bpf_get_current_task_btf(); 156 if (!task) 157 return 0; 158 159 bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb, NULL); 160 return 0; 161 } 162 163 SEC("lsm.s/file_open") 164 __success 165 int task_work_sleepable_prog(void *ctx) 166 { 167 struct task_work_elem *val; 168 struct task_struct *task; 169 int key = 0; 170 171 val = bpf_map_lookup_elem(&task_work_map, &key); 172 if (!val) 173 return 0; 174 175 task = bpf_get_current_task_btf(); 176 if (!task) 177 return 0; 178 179 bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb, NULL); 180 return 0; 181 } 182