Верификатор BPF отклоняет программу XDP из-за заднего края, даже если используется pragma unroll

Итак, согласно заголовку, я пытаюсь загрузить программу XDP, когда неожиданно верификатор bpf плюет мне в лицо со знаменитой ошибкой back-edge:

libbpf: load bpf program failed: Invalid argument
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
back-edge from insn 271 to 69

libbpf: -- END LOG --
libbpf: failed to load program 'xdp_prog'

Несмотря на то, что единственный цикл for - с количеством итераций, известным во время компиляции - в моем ограниченном коде ebpf C охраняется pragma unroll. Вот фрагмент кода, показывающий затронутый цикл for, определенный внутри функции __alwais_inlined:

#pragma unroll
for (i = 0; i < 8; i++)
{
    int k = idx + i;
    mask = bpf_map_lookup_elem(&a_map, &k);
    if (!mask || (mask->an_idx == 0))
        return -1;

    *m_key = *key;
    foo(m_key, mask);  // an __alwais_inline func

    id = bpf_map_lookup_elem(&b_map, m_key);
    if (id)
    {
        *out_id = *id;
        return 0;
    }
}

Может быть, проблема в том, что clang не может развернуть цикл? Если это так, почему это не работает, есть ли обходной путь? Недопустимо разворачивать цикл вручную, так как это приводит к ужасающему, неподдерживаемому и нечитаемому коду.

О, я работаю с:

  • ядро 4.19.3
  • llvm-лязг 8

Какие-нибудь мысли?

ОБНОВЛЕНИЕ
Только что заметил, что даже следующий фиктивный цикл for, кажется, не развернут, а верификатор bpf жалуется на задний край:

#pragma unroll
for (i = 0; i < 8; i++)
{
    int k = i;
    mask = bpf_map_lookup_elem(&a_map, &k);
}

Это только мне для этого не имеет никакого смысла?


person pa5h1nh0    schedule 03.07.2019    source источник
comment
Почему «неудивительно», если вы используете прагму? Почему «очевидно», если вы не уверены на 100% :) Несколько замечаний: 1) Вы получили предупреждение во время компиляции? Я считаю, что clang печатает один, когда не может развернуть цикл. 2) Просматривали ли вы сгенерированные инструкции BPF (llvm-objdump -S objfile.o), чтобы убедиться, что задний фронт исходит из этого конкретного цикла? 3) Если вы гибки в версиях ядра, вам может быть интересно узнать, что linux 5.3 (в настоящее время находится в разработке) поддерживает ограниченные циклы.   -  person Qeole    schedule 03.07.2019
comment
Согласен с неудивительно на удивительно, также согласен с явно на возможно. Я бы отредактировал это в первую очередь. Отвечая на ваши вопросы: 1) нет предупреждения во время компиляции; 2) почти уверен, поскольку это единственный цикл, определенный в моем коде, а также потому, что проверка проходит, если закомментировать этот цикл for; 3) это решит проблему?   -  person pa5h1nh0    schedule 03.07.2019
comment
Найдите EINVAL в bpf_map_lookup_elem.   -  person S.S. Anne    schedule 03.07.2019
comment
3) обеспечивает поддержку циклов такого типа в верификаторе BPF, поэтому задний край для этого цикла больше не будет отклоняться. Так что да, это должно решить проблему. Хотя звучит как перебор. По моему опыту, я всегда получал предупреждения (как этот), когда clang не смог развернуться :/. Не могу больше помочь без кода C или BPF, извините. (Кроме того, в Linux 4.19 вам не нужно встраивать все функции, поддерживаются вызовы функций BPF. Но это не относится к проблеме.)   -  person Qeole    schedule 03.07.2019
comment
@ JL2210, что это вообще значит в данном случае?   -  person pa5h1nh0    schedule 04.07.2019
comment
@ pa5h1nh0 Посмотрите исходный код bpf_map_lookup_elem в дереве исходного кода ядра и найдите EINVAL.   -  person S.S. Anne    schedule 04.07.2019
comment
Я не думаю, что EINVAL возвращается bpf_map_lookup_elem, это не будет связано с задним краем? @ pa5h1nh0 Я часто вижу #pragma clang loop unroll(full) вместо #pragma unroll, я думал, что разницы нет, но, может, на всякий случай попробовать другой?   -  person Qeole    schedule 08.07.2019
comment
@Qeole да, действительно bpf_map_lookup_elem возвращает указатель, поэтому искать EINVAL для меня нет смысла. Уже попробовал #pragma clang loop unroll(full) версию, тот же результат, на самом деле в документации clang указано, что эти два слова являются синонимами. Atm Я вручную разворачиваю петлю, что противно, да ладно.   -  person pa5h1nh0    schedule 08.07.2019
comment
Не могли бы вы иметь где-нибудь автономный репродуктор, в идеале с Makefile, чтобы убедиться, что мы запускаем одни и те же команды, чтобы я мог попытаться воспроизвести на своей стороне?   -  person Qeole    schedule 10.07.2019
comment
Кстати, ваш пример с «фиктивной петлей» меня вполне устраивает :/.   -  person Qeole    schedule 11.07.2019


Ответы (1)


Я пишу следующий код, и он работает.

#pragma clang loop unroll(full)
for (int i = 0; i < 128; i++)
{
    if (bpf_map_lookup_elem(&conntrack_hash_tab, &reply_key) == NULL)
    {
        break;
    }
    reply_key.dport = reply_key.dport+1;
}
person keniee van    schedule 29.07.2019