После прочтения man bpf и нескольких других источников документации у меня сложилось впечатление, что map может быть создан только пользовательским процессом. Однако следующая небольшая программа, похоже, волшебным образом создает карту bpf:

struct bpf_map_def SEC("maps") my_map = {
        .type = BPF_MAP_TYPE_ARRAY,
        .key_size = sizeof(u32),
        .value_size = sizeof(long),
        .max_entries = 10,
};

SEC("sockops")
int my_prog(struct bpf_sock_ops *skops)
{
   u32 key = 1;
   long *value;
   ...

   value = bpf_map_lookup_elem(&my_map, &key);
   ...
   return 1;
}

Итак, я загружаю программу с tools/bpf/bpftool ядра, а также проверяю, что программа загружена:

$ bpftool prog show
1: sock_ops  name my_prog  tag f3a3583cdd82ae8d
        loaded_at Jan 02/18:46  uid 0
        xlated 728B  not jited  memlock 4096B

$ bpftool map show
1: array  name my_map  flags 0x0
        key 4B  value 8B  max_entries 10  memlock 4096B

Конечно карта пуста. Однако удаление bpf_map_lookup_elem из программы не приводит к созданию карты.

ОБНОВЛЕНИЕ Я отлаживал его с помощью strace и обнаружил, что в обоих случаях, то есть с bpf_map_lookup_elem и без него, bpftool действительно вызывает bpf(BPF_MAP_CREATE, ...) и, по-видимому, успешно. Затем, в случае отсутствия bpf_map_lookup_elem, я включаю bpftool map show, и bpf(BPF_MAP_GET_NEXT_ID, ..) немедленно возвращает ENOENT, и он никогда не сбрасывает карту. Очевидно, что что-то не завершает создание карты.

Так что мне интересно, ожидаемое ли такое поведение?

Спасибо.

5
Mark 2 Янв 2018 в 23:05

2 ответа

Лучший ответ

Как объяснил antiduh и подтвердил вашими проверками strace, bpftool - это программа пользовательского пространства, создающая карты в этом случае. Он вызывает функцию bpf_prog_load() из libbpf (под tools/lib/bpf/), которая, в свою очередь, выполняет системный вызов. Затем программа закрепляется в нужном месте (под точкой монтирования виртуальной файловой системы bpf), чтобы она не выгружалась при возврате из bpftool. Карты не закреплены.

Что касается создания карты, волшебные моменты также происходят в libbpf. Когда вызывается bpf_prog_load(), libbpf получает имя объектного файла в качестве аргумента. bpftool не требует загрузки этой конкретной программы или той конкретной карты; вместо этого он предоставляет объектный файл, и libbpf имеет дело с ним. Таким образом, функции в libbpf анализируют этот объектный файл ELF и в конечном итоге находят ряд разделов, соответствующих картам и программам. Затем он пытается загрузить первую программу.

Загрузка этой программы включает следующие шаги:

CHECK_ERR(bpf_object__create_maps(obj), err, out);
CHECK_ERR(bpf_object__relocate(obj), err, out);
CHECK_ERR(bpf_object__load_progs(obj), err, out);

Другими словами: начните с создания всех карт, которые мы нашли в объектном файле. Затем выполните перемещение карты (т. Е. Свяжите индекс карты с инструкциями eBPF) и, наконец, загрузите инструкции программы.

Итак, что касается вашего вопроса: в обоих случаях, с bpf_map_lookup_elem() и без него, карты создаются с помощью системного вызова bpf(BPF_MAP_CREATE, ...). После этого происходит перемещение, и инструкции программы адаптируются, чтобы при необходимости указывать на вновь созданные карты. Затем, когда все шаги завершены и программа загружена, bpftool завершает работу. Программа eBPF должна быть закреплена и по-прежнему загружена в ядро. Насколько я понимаю, если он действительно использует карты (если был использован bpf_map_lookup_elem()), то на карты по-прежнему ссылается загруженная программа и они сохраняются в ядре. С другой стороны, если программа не использует карты, то больше нечего сдерживать, поэтому карты уничтожаются, когда дескрипторы файлов, удерживаемые bpftool, закрываются, когда возвращается bpftool.

Итак, в конце, когда bpftool завершится, у вас будет карта, загруженная в ядро, если программа ее использует, но не карта, если никакая программа не будет на нее полагаться. На мой взгляд, похоже на ожидаемое поведение; но, пожалуйста, сделайте ping так или иначе, если у вас возникнут странные вещи с bpftool, я один из тех, кто работает над этой утилитой. Последнее общее наблюдение: карты также могут быть закреплены и оставаться в ядре, даже если никакая программа их не использует, на случай, если понадобится их сохранить.

7
Qeole 14 Янв 2019 в 16:20

У меня создалось впечатление, что карту может создать только пользовательский процесс.

Вы совершенно правы - именно программы пользователя вызывают системный вызов bpf для загрузки программ eBPF и создания карт eBPF.

И ты так и сделал:

Итак, я загружаю программу с помощью tools / bpf / bpftool и ...

Ваша программа bpftool - это пользовательский процесс, который вызывает системный вызов bpf и, таким образом, является пользовательским процессом, который создает карту eBPF.

Программы BPF не нужно выгружать, когда пользовательская программа, создавшая их, завершает работу - вероятно, bpftool использует этот механизм.

Некоторые соответствующие биты со страницы руководства, чтобы соединить точки:

Пользовательский процесс может создавать несколько карт ... и получать к ним доступ через файловые дескрипторы.

Обычно программы eBPF загружаются пользовательским процессом и автоматически выгружаются при выходе из процесса. В некоторых случаях ... программа будет продолжать оставаться в ядре даже после завершения процесса, загрузившего программу.

Каждая программа eBPF представляет собой набор инструкций, которые можно безопасно выполнять до завершения. ... Во время проверки ядро увеличивает счетчик ссылок для каждой карты, которую использует программа eBPF, так что прикрепленные карты нельзя удалить, пока программа не будет выгружена.

2
antiduh 2 Янв 2018 в 20:52