-
Notifications
You must be signed in to change notification settings - Fork 324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ebpf: replace the existing program on update #352
Conversation
4e6d583
to
d9aeb6b
Compare
src/libcrun/ebpf.c
Outdated
} | ||
|
||
memset (&attr, 0, sizeof (attr)); | ||
attr.attach_type = BPF_CGROUP_DEVICE; | ||
attr.target_fd = dirfd; | ||
attr.attach_bpf_fd = fd; | ||
#ifdef BPF_F_REPLACE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add kernel requirement (v5.6) in the code comment
torvalds/linux@7dd68b3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, I am going to rework the patch and handle better concurrent updates
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, I'll have to move the check at runtime, not build time
e00aba6
to
84949c9
Compare
LGTM |
const int MAX_ATTEMPTS = 20; | ||
int attempt; | ||
|
||
for (attempt = 0;; attempt++) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe attempt = 0; attempt < MAX_ATTEMPTS; attempt++
for readability
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to rewrite using the condition inside of the for loop but there is some extra logic that decides whether to retry or not.
cc @cyphar PTAL |
@cyphar LGTY? |
fine to merge? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is okay as a first draft, but I think we should rethink how we're generating these programs. It might be much nicer in the long term to store the device rules in an eBPF map (which we can atomically swap out if we use a map-of-maps and BPF_F_LOCK
). That way we would never have to replace the program, and could just update the eBPF map whenever we needed to. It would also allow us to read the current rule-set from userspace.
{ | ||
#ifdef BPF_F_REPLACE | ||
memset (&attr, 0, sizeof (attr)); | ||
attr.prog_id = progs[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic (replacing whatever program is attached) and the corresponding limitations (not being able to replace a program if there is more than one) seems a bit fishy. I think a much better solution would be to either pin the program, or to store the id and load-time into some state file (which I assume crun
has) and then to use that to fetch the original program.
To be fair, it's unlikely anybody else will be attaching BPF_CGROUP_DEVICE
programs to a container but this seems a bit fishy to me.
src/libcrun/ebpf.c
Outdated
return crun_make_error (err, errno, "cannot open existing eBPF program"); | ||
} | ||
#else | ||
return crun_make_error (err, 0, "eBPF program already configured"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If BPF_F_REPLACE
isn't supported -- which is the case for most kernels -- you should insert the new program then remove the old one. It won't be atomic, but it'll be much better than this.
Also you mentioned you'd do a |
Thanks for the review I had initially tried to implement the devices controller using an eBPF map, but I had issues dealing with wildcards and I am not sure how we could handle them without enumeration or using a more complex map of maps kind of data structure. Do you have any suggestions for it? In the current implementation, I've assumed that there is a single writer/owner for the cgroup, so there should never been two programs installed. That is the reason for the weird check. Multiple writers were somehow supported with pinning in the previous versions of this PR but there were also some potential race conditions with multiple In this version I've tried to deal with concurrent updates and avoid any potential of race conditions. Currently there is no fallback when Would the fallback for
What I don't like is that if something goes wrong we can end up with multiple eBPF programs installed that we lose track of. |
b31711d
to
42adc81
Compare
I've changed the implementation and pushed a new version. It still assumes that crun is the only writer to the cgroup, any other eBPF program is removed on an update.
In any other case, now crun first stores what programs are already installed for the cgroup, attaches the new program, then it removes all the programs previously installed. Multiple |
@cyphar LGTY? |
@cyphar PTAL |
Signed-off-by: Giuseppe Scrivano <[email protected]>
on a container update, make sure the existing eBPF programs are completely replaced. Signed-off-by: Giuseppe Scrivano <[email protected]>
@rhatdan this is ready to go, if there are other comments we can address them later. |
LGTM |
on a container update, make sure the existing eBPF program is
completely replaced.
Signed-off-by: Giuseppe Scrivano [email protected]