Skip to content
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

dnsdist: Raise RLIMIT_MEMLOCK automatically when eBPF is requested #11554

Merged

Conversation

yog-singh
Copy link
Contributor

@yog-singh yog-singh commented Apr 17, 2022

Short Description:

Raise RLIMIT_MEMLOCK automatically when eBPF is requested.

#10566
This PR adds changes to eBPF filter constructor which when invoked automatically raises the RLIMIT_MEMLOCK from 64k to 1024k.
The hard limit for the user needs to be set in /etc/security/limits.conf.

Checklist

I have:

  • read the CONTRIBUTING.md document
  • compiled this code
  • tested this code
  • included documentation (including possible behaviour changes)
  • documented the code
  • added or modified regression test(s)
  • added or modified unit test(s)
  • checked that this code was merged to master

@rgacogne rgacogne added this to the dnsdist-1.8.0 milestone Apr 17, 2022
@rgacogne rgacogne self-requested a review April 17, 2022 12:04
Copy link
Member

@rgacogne rgacogne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good, I made a few suggestions that are pretty much all around logging, nothing serious. Thanks a lot!

@yog-singh yog-singh force-pushed the yog-singh/ddist-ebpf-memlock-limit branch 4 times, most recently from 423cc11 to f24884b Compare April 17, 2022 13:05
@yog-singh yog-singh requested a review from rgacogne April 17, 2022 13:15
@yog-singh yog-singh force-pushed the yog-singh/ddist-ebpf-memlock-limit branch from f24884b to 569787b Compare April 17, 2022 13:23
@@ -320,6 +322,25 @@ BPFFilter::BPFFilter(std::unordered_map<std::string, MapConfiguration>& configs,
throw std::runtime_error("Unsupported eBPF map format, the current internal implemenation only supports the legacy format");
}

struct rlimit old_limit;
const size_t new_limit_size = 1073741824;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, if the target is 1024k, should that number be 1048576? And for the 64k check, 65536 instead of 67108865? If so, I guess we also need to change the $USER hard memlock 1073741824 line in the documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rlim_cur and rlim_max returned by getrlimit is a multiplied value of 64k, actually multiplying it by 1024. While what we set in limit.conf is exact value as you mentioned. So I should change in the documentation, but will check in the code what the getrlimit function call returns and why is the value different from what I get from ulimit -a

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ulimit uses memory sizes in kbytes. getrlimit and setrlimit use bytes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused, are we trying to raise the limit to 1024 kilobytes or more? If we are trying to raise it to 1024 kilobytes, I believe we need to replace 1024 * 1024 * 1024 by 1024 * 1024 and, 64 * 1024 * 1024 by 64 * 1024, right?

@yog-singh yog-singh force-pushed the yog-singh/ddist-ebpf-memlock-limit branch from 569787b to 5ac199f Compare April 17, 2022 16:54
@yog-singh yog-singh requested a review from rgacogne April 17, 2022 16:55
@yog-singh yog-singh force-pushed the yog-singh/ddist-ebpf-memlock-limit branch from 5ac199f to e571a15 Compare April 20, 2022 18:47
@yog-singh yog-singh requested a review from omoerbeek April 20, 2022 18:48
throw std::runtime_error("Unable to get memory lock limit: " + stringerror());
}

/* Check if the current soft memlock limit is 64k */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments and code do not match. k or M?

/* Check if the current soft memlock limit is 64k */
if (old_limit.rlim_cur < (64 * 1024 * 1024)) {
struct rlimit new_limit;
new_limit.rlim_cur = new_limit_size; /* Increase soft limit to 1024k */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: code and comment do not match.

if (old_limit.rlim_cur < (64 * 1024 * 1024)) {
struct rlimit new_limit;
new_limit.rlim_cur = new_limit_size; /* Increase soft limit to 1024k */
new_limit.rlim_max = new_limit_size; /* Increase hard limit to 1024k */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rasing the hard limit will fail for non-root. Or are we always root at this point?

Copy link
Contributor Author

@yog-singh yog-singh May 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For eBPF filter I think we need to be root at this point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might only have CAP_BPF at this point, but failing is OK as long as we keep going on IMHO.

@@ -73,3 +73,6 @@ Since 1.6.0, the default BPF filter set via :func:`setDefaultBPFFilter` will aut
That feature might require an increase of the memory limit associated to a socket, via the sysctl setting ``net.core.optmem_max``.
When attaching an eBPF program to a socket, the size of the program is checked against this limit, and the default value might not be enough.
Large map sizes might also require an increase of ``RLIMIT_MEMLOCK``, which can be done by adding ``LimitMEMLOCK=infinity`` in the systemd unit file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like infinity limits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not going ahead with infinity limit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

infinity is still mentioned on line 75.

Also: there is a unit confusion here: the code in bpf-filter.cc uses 1 mega byte (setrlimit's unit is bytes), while the /security/limits.conf example set the limit to 1GB (it uses 1k byte as unit).
Note that LimitMEMLOCK in a systemd unit file uses bytes as unit.

@yog-singh yog-singh force-pushed the yog-singh/ddist-ebpf-memlock-limit branch from e571a15 to 877673c Compare May 29, 2022 19:09
@yog-singh yog-singh requested a review from omoerbeek June 3, 2022 07:49
@yog-singh yog-singh force-pushed the yog-singh/ddist-ebpf-memlock-limit branch from 877673c to 8f29b09 Compare June 16, 2022 03:08
@omoerbeek omoerbeek force-pushed the yog-singh/ddist-ebpf-memlock-limit branch from 8f29b09 to d418ba4 Compare December 7, 2022 08:30
Raise RLIMIT_MEMLOCK automatically when eBPF is requested.

This PR adds changes to eBPF filter constructor which when invoked automatically raises the RLIMIT_MEMLOCK from 64k to 1024k.
The hard limit for the user needs to be set in `/etc/security/limits.conf`.
@omoerbeek omoerbeek force-pushed the yog-singh/ddist-ebpf-memlock-limit branch from d418ba4 to dc22be5 Compare December 7, 2022 08:32
@omoerbeek
Copy link
Member

Rebased to fix conflicts and adapted slightly.

}

/* Check if the current soft memlock limit is 64k */
if (old_limit.rlim_cur < (64 * 1024)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we will only raise the limit when it is below 64k, right? But we know 64k is not enough, so perhaps it would make sense to try to raise it if it is below 1024k instead?

if (old_limit.rlim_cur < (64 * 1024 * 1024)) {
struct rlimit new_limit;
new_limit.rlim_cur = new_limit_size; /* Increase soft limit to 1024k */
new_limit.rlim_max = new_limit_size; /* Increase hard limit to 1024k */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might only have CAP_BPF at this point, but failing is OK as long as we keep going on IMHO.

new_limit.rlim_max = new_limit_size; /* Increase hard limit to 1024k */

if (setrlimit(RLIMIT_MEMLOCK, &new_limit) != 0) {
errlog("Unable to raise the maximum amount of locked memory for eBPF from %d to %d, consider raising RLIMIT_MEMLOCK or setting LimitMEMLOCK=infinity in the systemd unit: %s", old_limit.rlim_cur, new_limit.rlim_cur, stringerror());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
errlog("Unable to raise the maximum amount of locked memory for eBPF from %d to %d, consider raising RLIMIT_MEMLOCK or setting LimitMEMLOCK=infinity in the systemd unit: %s", old_limit.rlim_cur, new_limit.rlim_cur, stringerror());
warnlog("Unable to raise the maximum amount of locked memory for eBPF from %d to %d, consider raising RLIMIT_MEMLOCK or setting LimitMEMLOCK=infinity in the systemd unit: %s", old_limit.rlim_cur, new_limit.rlim_cur, stringerror());

if (setrlimit(RLIMIT_MEMLOCK, &new_limit) != 0) {
errlog("Unable to raise the maximum amount of locked memory for eBPF from %d to %d, consider raising RLIMIT_MEMLOCK or setting LimitMEMLOCK=infinity in the systemd unit: %s", old_limit.rlim_cur, new_limit.rlim_cur, stringerror());
}
infolog("The current limit of locked memory (soft: %d, hard: %d) is too low for eBPF, trying to raise it to %d", old_limit.rlim_cur, old_limit.rlim_max, new_limit_size);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should move that comment before the setrlimit call, otherwise we might log the failure before the attempt.

@omoerbeek omoerbeek force-pushed the yog-singh/ddist-ebpf-memlock-limit branch from 1e48578 to ad97958 Compare December 7, 2022 10:08
@omoerbeek omoerbeek requested a review from rgacogne December 7, 2022 10:09
const rlim_t new_limit_size = 1024 * 1024;

/* Check if the current soft memlock limit is at least minimal_limit */
if (old_limit.rlim_cur < minimal_limit_size) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not very useful to raise the limit if the current value is below 64k, as most systems set RLIMIT_MEMLOCK to exactly 64k, and we know this is not enough for most usage (decent-sized maps). How about we try to raise that value to 1024k if it is below 1024k?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense indeed

@omoerbeek omoerbeek requested a review from rgacogne December 7, 2022 13:10
Copy link
Member

@rgacogne rgacogne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good, thanks!

@omoerbeek
Copy link
Member

One unrelated CI failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants