@@ -293,15 +293,169 @@ bpf_program_complete_dev (struct bpf_program *program, libcrun_error_t *err arg_
293
293
return program ;
294
294
}
295
295
296
+ static int
297
+ read_all_progs (int dirfd , uint32_t * * progs_out , size_t * n_progs_out , libcrun_error_t * err )
298
+ {
299
+ cleanup_free uint32_t * progs = NULL ;
300
+ union bpf_attr attr ;
301
+ int ret ;
302
+ size_t cur_size ;
303
+
304
+ /* The kernel 5.7 has a hard limit of 64, let's be safe if the limit
305
+ will be increased in future and attempt up to 4096. */
306
+ for (cur_size = 64 ; cur_size <= 4096 ; cur_size *= 2 )
307
+ {
308
+ progs = xrealloc (progs , sizeof (uint32_t ) * cur_size );
309
+
310
+ memset (& attr , 0 , sizeof (attr ));
311
+ attr .query .target_fd = dirfd ;
312
+ attr .query .attach_type = BPF_CGROUP_DEVICE ;
313
+ attr .query .prog_cnt = cur_size ;
314
+ attr .query .prog_ids = (uint64_t ) progs ;
315
+
316
+ ret = bpf (BPF_PROG_QUERY , & attr , sizeof (attr ));
317
+ }
318
+ while (ret < 0 && errno == ENOSPC );
319
+
320
+ if (UNLIKELY (ret < 0 ))
321
+ return crun_make_error (err , errno , "bpf query" );
322
+
323
+ * progs_out = progs ;
324
+ progs = NULL ;
325
+ * n_progs_out = attr .query .prog_cnt ;
326
+ return 0 ;
327
+ }
328
+
329
+ static int
330
+ remove_all_progs (int dirfd , uint32_t * progs , size_t n_progs , libcrun_error_t * err )
331
+ {
332
+ union bpf_attr attr ;
333
+ size_t i ;
334
+
335
+ for (i = 0 ; i < n_progs ; i ++ )
336
+ {
337
+ cleanup_close int fd = -1 ;
338
+ int ret ;
339
+
340
+ memset (& attr , 0 , sizeof (attr ));
341
+ attr .prog_id = progs [i ];
342
+ fd = bpf (BPF_PROG_GET_FD_BY_ID , & attr , sizeof (attr ));
343
+ if (UNLIKELY (fd < 0 ))
344
+ {
345
+ /* Already removed. Nothing to do. */
346
+ if (errno == ENOENT )
347
+ continue ;
348
+
349
+ return crun_make_error (err , errno , "cannot open existing eBPF program" );
350
+ }
351
+
352
+ memset (& attr , 0 , sizeof (attr ));
353
+ attr .attach_type = BPF_CGROUP_DEVICE ;
354
+ attr .target_fd = dirfd ;
355
+ attr .attach_bpf_fd = fd ;
356
+
357
+ ret = bpf (BPF_PROG_DETACH , & attr , sizeof (attr ));
358
+ if (UNLIKELY (ret < 0 ))
359
+ {
360
+ /* Already removed. Nothing to do. */
361
+ if (errno == ENOENT )
362
+ continue ;
363
+
364
+ return crun_make_error (err , errno , "cannot detach eBPF program" );
365
+ }
366
+ }
367
+ return 0 ;
368
+ }
369
+
370
+ static int
371
+ ebpf_attach_program (int fd , int dirfd , libcrun_error_t * err )
372
+ {
373
+ #ifdef BPF_F_REPLACE
374
+ bool skip_replace = false;
375
+ #endif
376
+ const int MAX_ATTEMPTS = 20 ;
377
+ int attempt ;
378
+
379
+ for (attempt = 0 ;; attempt ++ )
380
+ {
381
+ cleanup_free uint32_t * progs = NULL ;
382
+ cleanup_close int replacefd = -1 ;
383
+ union bpf_attr attr ;
384
+ size_t n_progs = 0 ;
385
+ int ret ;
386
+
387
+ ret = read_all_progs (dirfd , & progs , & n_progs , err );
388
+ if (UNLIKELY (ret < 0 ))
389
+ return ret ;
390
+
391
+ #ifdef BPF_F_REPLACE
392
+ /* There is just one program installed, let's attempt an atomic replace if supported. */
393
+ if (!skip_replace && n_progs == 1 )
394
+ {
395
+ memset (& attr , 0 , sizeof (attr ));
396
+ attr .prog_id = progs [0 ];
397
+ replacefd = bpf (BPF_PROG_GET_FD_BY_ID , & attr , sizeof (attr ));
398
+ if (UNLIKELY (replacefd < 0 ))
399
+ {
400
+ if (errno == ENOENT && attempt < MAX_ATTEMPTS )
401
+ {
402
+ /* Another update might have raced and updated, try again. */
403
+ continue ;
404
+ }
405
+ return crun_make_error (err , errno , "cannot open existing eBPF program" );
406
+ }
407
+ }
408
+ #endif
409
+
410
+ memset (& attr , 0 , sizeof (attr ));
411
+ attr .attach_type = BPF_CGROUP_DEVICE ;
412
+ attr .target_fd = dirfd ;
413
+ attr .attach_bpf_fd = fd ;
414
+ attr .attach_flags = BPF_F_ALLOW_MULTI ;
415
+ #ifdef BPF_F_REPLACE
416
+ if (replacefd >= 0 )
417
+ {
418
+ attr .attach_flags = BPF_F_ALLOW_MULTI | BPF_F_REPLACE ;
419
+ attr .replace_bpf_fd = replacefd ;
420
+ }
421
+ #endif
422
+
423
+ ret = bpf (BPF_PROG_ATTACH , & attr , sizeof (attr ));
424
+ if (UNLIKELY (ret < 0 ))
425
+ {
426
+ if (errno == ENOENT && replacefd >= 0 && attempt < MAX_ATTEMPTS )
427
+ {
428
+ /* Another update might have already updated the cgroup, try again. */
429
+ continue ;
430
+ }
431
+ #ifdef BPF_F_REPLACE
432
+ if (errno == EINVAL && replacefd >= 0 )
433
+ {
434
+ skip_replace = true;
435
+ continue ;
436
+ }
437
+ #endif
438
+ return crun_make_error (err , errno , "bpf attach" );
439
+ }
440
+
441
+ /* Now that the new program is installed, remove all the programs that were previously installed. */
442
+ if (replacefd < 0 && n_progs )
443
+ return remove_all_progs (dirfd , progs , n_progs , err );
444
+
445
+ return 0 ;
446
+ }
447
+ }
448
+
296
449
int
297
450
libcrun_ebpf_load (struct bpf_program * program , int dirfd , const char * pin , libcrun_error_t * err )
298
451
{
299
452
#ifndef HAVE_EBPF
300
453
return crun_make_error (err , 0 , "eBPF not supported" );
301
454
#else
302
- int fd , ret ;
455
+ cleanup_close int fd = -1 ;
303
456
union bpf_attr attr ;
304
457
struct rlimit limit ;
458
+ int ret ;
305
459
306
460
limit .rlim_cur = RLIM_INFINITY ;
307
461
limit .rlim_max = RLIM_INFINITY ;
@@ -329,30 +483,26 @@ libcrun_ebpf_load (struct bpf_program *program, int dirfd, const char *pin, libc
329
483
330
484
fd = bpf (BPF_PROG_LOAD , & attr , sizeof (attr ));
331
485
if (fd < 0 )
332
- return crun_make_error (err , errno , "bpf create %s " , log );
486
+ return crun_make_error (err , errno , "bpf create `%s` " , log );
333
487
}
334
488
335
- memset (& attr , 0 , sizeof (attr ));
336
- attr .attach_type = BPF_CGROUP_DEVICE ;
337
- attr .target_fd = dirfd ;
338
- attr .attach_bpf_fd = fd ;
339
- attr .attach_flags = BPF_F_ALLOW_MULTI ;
340
-
341
- ret = bpf (BPF_PROG_ATTACH , & attr , sizeof (attr ));
342
- if (ret < 0 )
343
- return crun_make_error (err , errno , "bpf attach" );
489
+ ret = ebpf_attach_program (fd , dirfd , err );
490
+ if (UNLIKELY (ret < 0 ))
491
+ return ret ;
344
492
345
493
/* Optionally pin the program to the specified path. */
346
494
if (pin )
347
495
{
496
+ unlink (pin );
497
+
348
498
memset (& attr , 0 , sizeof (attr ));
349
499
attr .pathname = (uint64_t ) pin ;
350
500
attr .bpf_fd = fd ;
351
501
ret = bpf (BPF_OBJ_PIN , & attr , sizeof (attr ));
352
502
if (ret < 0 )
353
- return crun_make_error (err , errno , "bpf pin to %s " , pin );
503
+ return crun_make_error (err , errno , "bpf pin to `%s` " , pin );
354
504
}
355
505
356
- return fd ;
506
+ return 0 ;
357
507
#endif
358
508
}
0 commit comments