@@ -299,9 +299,14 @@ libcrun_ebpf_load (struct bpf_program *program, int dirfd, const char *pin, libc
299
299
#ifndef HAVE_EBPF
300
300
return crun_make_error (err , 0 , "eBPF not supported" );
301
301
#else
302
- int fd , ret ;
302
+ cleanup_close int replacefd = -1 ;
303
+ cleanup_close int fd = -1 ;
304
+ union bpf_attr attr_open ;
305
+ int attempts_left = 20 ;
303
306
union bpf_attr attr ;
304
307
struct rlimit limit ;
308
+ uint32_t progs [2 ];
309
+ int ret ;
305
310
306
311
limit .rlim_cur = RLIM_INFINITY ;
307
312
limit .rlim_max = RLIM_INFINITY ;
@@ -329,28 +334,84 @@ libcrun_ebpf_load (struct bpf_program *program, int dirfd, const char *pin, libc
329
334
330
335
fd = bpf (BPF_PROG_LOAD , & attr , sizeof (attr ));
331
336
if (fd < 0 )
332
- return crun_make_error (err , errno , "bpf create %s" , log );
337
+ return crun_make_error (err , errno , "bpf create `%s`" , log );
338
+ }
339
+
340
+ repeat :
341
+ memset (& attr_open , 0 , sizeof (attr_open ));
342
+ attr_open .query .target_fd = dirfd ;
343
+ attr_open .query .attach_type = BPF_CGROUP_DEVICE ;
344
+ attr_open .query .prog_cnt = sizeof (progs ) / sizeof (progs [0 ]);
345
+ attr_open .query .prog_ids = (uint64_t ) & progs ;
346
+
347
+ ret = bpf (BPF_PROG_QUERY , & attr_open , sizeof (attr_open ));
348
+ if (UNLIKELY (ret < 0 ))
349
+ return crun_make_error (err , errno , "bpf query" );
350
+
351
+ if (attr_open .query .prog_cnt > 1 )
352
+ return crun_make_error (err , 0 , "invalid device cgroup state, more than one program installed" );
353
+
354
+ if (attr_open .query .prog_cnt == 1 )
355
+ {
356
+ #ifdef BPF_F_REPLACE
357
+ memset (& attr_open , 0 , sizeof (attr_open ));
358
+ attr_open .prog_id = progs [0 ];
359
+ replacefd = bpf (BPF_PROG_GET_FD_BY_ID , & attr_open , sizeof (attr_open ));
360
+ if (UNLIKELY (replacefd < 0 ))
361
+ {
362
+ if (errno == ENOENT && attempts_left > 0 )
363
+ {
364
+ /* Another update might have raced and updated, try again. */
365
+ close_and_reset (& replacefd );
366
+
367
+ attempts_left -- ;
368
+ goto repeat ;
369
+ }
370
+ return crun_make_error (err , errno , "cannot open existing eBPF program" );
371
+ }
372
+ #else
373
+ return crun_make_error (err , 0 , "eBPF program already configured" );
374
+ #endif
333
375
}
334
376
335
377
memset (& attr , 0 , sizeof (attr ));
336
378
attr .attach_type = BPF_CGROUP_DEVICE ;
337
379
attr .target_fd = dirfd ;
338
380
attr .attach_bpf_fd = fd ;
381
+ #ifdef BPF_F_REPLACE
382
+ attr .attach_flags = BPF_F_ALLOW_MULTI | ((replacefd >= 0 ) ? BPF_F_REPLACE : 0 );
383
+ attr .replace_bpf_fd = replacefd ;
384
+ #else
339
385
attr .attach_flags = BPF_F_ALLOW_MULTI ;
386
+ #endif
340
387
341
388
ret = bpf (BPF_PROG_ATTACH , & attr , sizeof (attr ));
342
- if (ret < 0 )
343
- return crun_make_error (err , errno , "bpf attach" );
389
+ if (UNLIKELY (ret < 0 ))
390
+ {
391
+ if (errno == ENOENT && replacefd >= 0 && attempts_left > 0 )
392
+ {
393
+ /* Another update might have already updated the cgroup, try again. */
394
+ close_and_reset (& replacefd );
395
+
396
+ attempts_left -- ;
397
+ goto repeat ;
398
+ }
399
+ if (errno == EINVAL && replacefd >= 0 )
400
+ return crun_make_error (err , errno , "bpf attach. The kernel might not support BPF_F_REPLACE" );
401
+ return crun_make_error (err , errno , "bpf attach" );
402
+ }
344
403
345
404
/* Optionally pin the program to the specified path. */
346
405
if (pin )
347
406
{
407
+ unlink (pin );
408
+
348
409
memset (& attr , 0 , sizeof (attr ));
349
410
attr .pathname = (uint64_t ) pin ;
350
411
attr .bpf_fd = fd ;
351
412
ret = bpf (BPF_OBJ_PIN , & attr , sizeof (attr ));
352
413
if (ret < 0 )
353
- return crun_make_error (err , errno , "bpf pin to %s " , pin );
414
+ return crun_make_error (err , errno , "bpf pin to `%s` " , pin );
354
415
}
355
416
356
417
return fd ;
0 commit comments