From: Martynas Pumputis <m@lambda.lt>
To: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: bpf <bpf@vger.kernel.org>, Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Joe Stringer <joe@wand.net.nz>
Subject: Re: [PATCH bpf] libbpf: fix race when pinning maps in parallel
Date: Thu, 8 Jul 2021 17:52:57 +0200 [thread overview]
Message-ID: <4f2a546f-8d78-df2e-69eb-75055ff4137d@lambda.lt> (raw)
In-Reply-To: <CAEf4BzaHCgNSfoEVXkBweycHtVj2MKBBH45aZy+FM-BTjSJ3kA@mail.gmail.com>
On 7/8/21 12:38 AM, Andrii Nakryiko wrote:
> On Mon, Jul 5, 2021 at 12:08 PM Martynas Pumputis <m@lambda.lt> wrote:
>>
>> When loading in parallel multiple programs which use the same to-be
>> pinned map, it is possible that two instances of the loader will call
>> bpf_object__create_maps() at the same time. If the map doesn't exist
>> when both instances call bpf_object__reuse_map(), then one of the
>> instances will fail with EEXIST when calling bpf_map__pin().
>>
>> Fix the race by retrying creating a map if bpf_map__pin() returns
>> EEXIST. The fix is similar to the one in iproute2: e4c4685fd6e4 ("bpf:
>> Fix race condition with map pinning").
>>
>> Cc: Joe Stringer <joe@wand.net.nz>
>> Signed-off-by: Martynas Pumputis <m@lambda.lt>
>> ---
>> tools/lib/bpf/libbpf.c | 8 +++++++-
>> 1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 1e04ce724240..7a31c7c3cd21 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -4616,10 +4616,12 @@ bpf_object__create_maps(struct bpf_object *obj)
>> char *cp, errmsg[STRERR_BUFSIZE];
>> unsigned int i, j;
>> int err;
>> + bool retried = false;
>
> retried has to be reset for each map, so just move it inside the for
> loop? you can also generalize it to retry_cnt (> 1 attempts) to allow
> for more extreme cases of multiple loaders fighting very heavily
If we move "retried = false" to inside the loop, then there is no need
for retry_cnt. Single retry for each map should be enough to resolve the
race. In any case, I'm going to move "retried = false", as you suggested.
>
>>
>> for (i = 0; i < obj->nr_maps; i++) {
>> map = &obj->maps[i];
>>
>> +retry:
>> if (map->pin_path) {
>> err = bpf_object__reuse_map(map);
>> if (err) {
>> @@ -4660,9 +4662,13 @@ bpf_object__create_maps(struct bpf_object *obj)
>> if (map->pin_path && !map->pinned) {
>> err = bpf_map__pin(map, NULL);
>> if (err) {
>> + zclose(map->fd);
>> + if (!retried && err == EEXIST) {
>
> so I'm also wondering... should we commit at this point to trying to
> pin and not attempt to re-create the map? I'm worried that
> bpf_object__create_map() is not designed and tested to be called
> multiple times for the same bpf_map, but it's technically possible for
> it to be called multiple times in this scenario. Check the inner map
Good call. I'm going to add "if (retried && map->fd < 0) { return
-ENOENT; }" after the "if (map->pinned) { err = bpf_object__reuse_map()
... }" statement. This should prevent from invoking
bpf_object__create_map() multiple times.
> creation scenario, for example (btw, I think there is a bug in
> bpf_object__create_map clean up for inner map, care to take a look at
> that as well?).
In the case of the inner map, it should be destroyed inside
bpf_object__create_map() after a successful BPF_MAP_CREATE. So AFAIU,
there should be no need for the cleanup. Or do I miss something?
>
> So unless we want to allow map re-creation if (in a highly unlikely
> scenario) someone already unpinned the other instance, I'd say we
> should just bpf_map__pin() here directly, maybe in a short loop to
> allow for a few attempts.
>
>> + retried = true;
>> + goto retry;
>> + }
>> pr_warn("map '%s': failed to auto-pin at '%s': %d\n",
>> map->name, map->pin_path, err);
>> - zclose(map->fd);
>> goto err_out;
>> }
>> }
>> --
>> 2.32.0
>>
next prev parent reply other threads:[~2021-07-08 15:50 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-07-05 19:09 [PATCH bpf] libbpf: fix race when pinning maps in parallel Martynas Pumputis
2021-07-07 22:38 ` Andrii Nakryiko
2021-07-08 15:52 ` Martynas Pumputis [this message]
2021-07-08 20:33 ` Andrii Nakryiko
2021-07-15 10:17 ` Martynas Pumputis
2021-07-22 13:56 ` Martynas Pumputis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4f2a546f-8d78-df2e-69eb-75055ff4137d@lambda.lt \
--to=m@lambda.lt \
--cc=andrii.nakryiko@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=joe@wand.net.nz \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.