@@ -1109,11 +1109,18 @@ struct bpf_hrtimer {
11091109 struct hrtimer timer ;
11101110};
11111111
1112- /* the actual struct hidden inside uapi struct bpf_timer */
1112+ struct bpf_work {
1113+ struct bpf_async_cb cb ;
1114+ struct work_struct work ;
1115+ struct work_struct delete_work ;
1116+ };
1117+
1118+ /* the actual struct hidden inside uapi struct bpf_timer and bpf_wq */
11131119struct bpf_async_kern {
11141120 union {
11151121 struct bpf_async_cb * cb ;
11161122 struct bpf_hrtimer * timer ;
1123+ struct bpf_work * work ;
11171124 };
11181125 /* bpf_spin_lock is used here instead of spinlock_t to make
11191126 * sure that it always fits into space reserved by struct bpf_timer
@@ -1124,6 +1131,7 @@ struct bpf_async_kern {
11241131
11251132enum bpf_async_type {
11261133 BPF_ASYNC_TYPE_TIMER = 0 ,
1134+ BPF_ASYNC_TYPE_WQ ,
11271135};
11281136
11291137static DEFINE_PER_CPU (struct bpf_hrtimer * , hrtimer_running ) ;
@@ -1167,11 +1175,64 @@ static enum hrtimer_restart bpf_timer_cb(struct hrtimer *hrtimer)
11671175 return HRTIMER_NORESTART ;
11681176}
11691177
1178+ static void bpf_wq_work (struct work_struct * work )
1179+ {
1180+ struct bpf_work * w = container_of (work , struct bpf_work , work );
1181+ struct bpf_tramp_run_ctx __maybe_unused run_ctx ;
1182+ struct bpf_async_cb * cb = & w -> cb ;
1183+ struct bpf_prog * prog = cb -> prog ;
1184+ struct bpf_map * map = cb -> map ;
1185+ bpf_callback_t callback_fn ;
1186+ void * value = cb -> value ;
1187+ void * key ;
1188+ u32 idx ;
1189+
1190+ BTF_TYPE_EMIT (struct bpf_wq );
1191+
1192+ callback_fn = READ_ONCE (cb -> callback_fn );
1193+ if (!callback_fn || !prog )
1194+ return ;
1195+
1196+ if (map -> map_type == BPF_MAP_TYPE_ARRAY ) {
1197+ struct bpf_array * array = container_of (map , struct bpf_array , map );
1198+
1199+ /* compute the key */
1200+ idx = ((char * )value - array -> value ) / array -> elem_size ;
1201+ key = & idx ;
1202+ } else { /* hash or lru */
1203+ key = value - round_up (map -> key_size , 8 );
1204+ }
1205+
1206+ run_ctx .bpf_cookie = 0 ;
1207+
1208+ if (!__bpf_prog_enter_sleepable_recur (prog , & run_ctx )) {
1209+ /* recursion detected */
1210+ __bpf_prog_exit_sleepable_recur (prog , 0 , & run_ctx );
1211+ return ;
1212+ }
1213+
1214+ callback_fn ((u64 )(long )map , (u64 )(long )key , (u64 )(long )value , 0 , 0 );
1215+ /* The verifier checked that return value is zero. */
1216+
1217+ __bpf_prog_exit_sleepable_recur (prog , 0 /* bpf_prog_run does runtime stats */ ,
1218+ & run_ctx );
1219+ }
1220+
1221+ static void bpf_wq_delete_work (struct work_struct * work )
1222+ {
1223+ struct bpf_work * w = container_of (work , struct bpf_work , delete_work );
1224+
1225+ cancel_work_sync (& w -> work );
1226+
1227+ kfree_rcu (w , cb .rcu );
1228+ }
1229+
11701230static int __bpf_async_init (struct bpf_async_kern * async , struct bpf_map * map , u64 flags ,
11711231 enum bpf_async_type type )
11721232{
11731233 struct bpf_async_cb * cb ;
11741234 struct bpf_hrtimer * t ;
1235+ struct bpf_work * w ;
11751236 clockid_t clockid ;
11761237 size_t size ;
11771238 int ret = 0 ;
@@ -1183,6 +1244,9 @@ static int __bpf_async_init(struct bpf_async_kern *async, struct bpf_map *map, u
11831244 case BPF_ASYNC_TYPE_TIMER :
11841245 size = sizeof (struct bpf_hrtimer );
11851246 break ;
1247+ case BPF_ASYNC_TYPE_WQ :
1248+ size = sizeof (struct bpf_work );
1249+ break ;
11861250 default :
11871251 return - EINVAL ;
11881252 }
@@ -1201,13 +1265,22 @@ static int __bpf_async_init(struct bpf_async_kern *async, struct bpf_map *map, u
12011265 goto out ;
12021266 }
12031267
1204- if (type == BPF_ASYNC_TYPE_TIMER ) {
1268+ switch (type ) {
1269+ case BPF_ASYNC_TYPE_TIMER :
12051270 clockid = flags & (MAX_CLOCKS - 1 );
12061271 t = (struct bpf_hrtimer * )cb ;
12071272
12081273 hrtimer_init (& t -> timer , clockid , HRTIMER_MODE_REL_SOFT );
12091274 t -> timer .function = bpf_timer_cb ;
12101275 cb -> value = (void * )async - map -> record -> timer_off ;
1276+ break ;
1277+ case BPF_ASYNC_TYPE_WQ :
1278+ w = (struct bpf_work * )cb ;
1279+
1280+ INIT_WORK (& w -> work , bpf_wq_work );
1281+ INIT_WORK (& w -> delete_work , bpf_wq_delete_work );
1282+ cb -> value = (void * )async - map -> record -> wq_off ;
1283+ break ;
12111284 }
12121285 cb -> map = map ;
12131286 cb -> prog = NULL ;
@@ -1473,7 +1546,19 @@ void bpf_timer_cancel_and_free(void *val)
14731546 */
14741547void bpf_wq_cancel_and_free (void * val )
14751548{
1549+ struct bpf_work * work ;
1550+
14761551 BTF_TYPE_EMIT (struct bpf_wq );
1552+
1553+ work = (struct bpf_work * )__bpf_async_cancel_and_free (val );
1554+ if (!work )
1555+ return ;
1556+ /* Trigger cancel of the sleepable work, but *do not* wait for
1557+ * it to finish if it was running as we might not be in a
1558+ * sleepable context.
1559+ * kfree will be called once the work has finished.
1560+ */
1561+ schedule_work (& work -> delete_work );
14771562}
14781563
14791564BPF_CALL_2 (bpf_kptr_xchg , void * , map_value , void * , ptr )
@@ -2612,6 +2697,20 @@ __bpf_kfunc void bpf_throw(u64 cookie)
26122697 WARN (1 , "A call to BPF exception callback should never return\n" );
26132698}
26142699
2700+ __bpf_kfunc int bpf_wq_init (struct bpf_wq * wq , void * p__map , unsigned int flags )
2701+ {
2702+ struct bpf_async_kern * async = (struct bpf_async_kern * )wq ;
2703+ struct bpf_map * map = p__map ;
2704+
2705+ BUILD_BUG_ON (sizeof (struct bpf_async_kern ) > sizeof (struct bpf_wq ));
2706+ BUILD_BUG_ON (__alignof__(struct bpf_async_kern ) != __alignof__(struct bpf_wq ));
2707+
2708+ if (flags )
2709+ return - EINVAL ;
2710+
2711+ return __bpf_async_init (async , map , flags , BPF_ASYNC_TYPE_WQ );
2712+ }
2713+
26152714__bpf_kfunc_end_defs ();
26162715
26172716BTF_KFUNCS_START (generic_btf_ids )
@@ -2689,6 +2788,7 @@ BTF_ID_FLAGS(func, bpf_dynptr_is_rdonly)
26892788BTF_ID_FLAGS (func , bpf_dynptr_size )
26902789BTF_ID_FLAGS (func , bpf_dynptr_clone )
26912790BTF_ID_FLAGS (func , bpf_modify_return_test_tp )
2791+ BTF_ID_FLAGS (func , bpf_wq_init )
26922792BTF_KFUNCS_END (common_btf_ids )
26932793
26942794static const struct btf_kfunc_id_set common_kfunc_set = {
0 commit comments