AF-XDP: Как передать ctx-›data_meta из ядра в пользовательское пространство?

Я хочу измерить задержку пакетов для моей программы AF-XDP. Я просматривал эту ссылку: https://github.com/xdp-project/xdp-project/blob/master/areas/arm64/xdp_for_tsn.org

и приспособил его к этому:

SEC("xdp_sock")
int xdp_sock_prog(struct xdp_md *ctx) {

    int index = ctx->rx_queue_index;

    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

    const unsigned long long kstamp = bpf_ktime_get_ns();

    if(data - sizeof(unsigned long long) <= data_end) {
        ctx->data_meta = ctx->data - sizeof(unsigned long long);
        memcpy(&ctx->data_meta, &kstamp, sizeof(unsigned long long));
    }
    ...
}

Затем доступ в пространстве пользователя происходит следующим образом:

static bool process_packet(struct xsk_socket_info *xsk, uint64_t addr, uint32_t len) {

    uint8_t *pkt = xsk_umem__get_data(xsk->umem->buffer, addr);
    uint8_t *pkt_meta = xsk_umem__get_data(xsk->umem->buffer, addr - sizeof(unsigned long long));

    const unsigned long long kstamp = (uint64_t) pkt_meta[7] << 56 | (uint64_t) pkt_meta[6] << 48 | (uint64_t) pkt_meta[5] << 40
                                        | (uint64_t) pkt_meta[4] << 32 | (uint64_t) pkt_meta[3] << 24 | (uint64_t) pkt_meta[2] << 16
                                        | (uint64_t) pkt_meta[1] << 8  | (uint64_t) pkt_meta[0];
...
}

Но я не могу загрузить XDP-программу в ядро:

Load XDP program...
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
0: (bf) r6 = r1
1: (61) r1 = *(u32 *)(r6 +16)
2: (63) *(u32 *)(r10 -4) = r1
3: (61) r7 = *(u32 *)(r6 +4)
4: (61) r8 = *(u32 *)(r6 +0)
5: (85) call bpf_ktime_get_ns#5
6: (bf) r1 = r8
7: (07) r1 += -8
8: (2d) if r1 > r7 goto pc+3
 R0_w=inv(id=0) R1_w=pkt(id=0,off=-8,r=0,imm=0) R6_w=ctx(id=0,off=0,imm=0) R7_w=pkt_end(id=0,off=0,imm=0) R8_w=pkt(id=0,off=0,r=0,imm=0) R10=fp0 fp-8=mmmm????
9: (63) *(u32 *)(r6 +8) = r0
invalid bpf_context access off=8 size=4
processed 10 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

libbpf: -- END LOG --

Я имею в виду, что это не должно быть возможно, верно? Поскольку XDP — это все о действительных доступах к памяти, и почему доступ за пределами определенных границ data и data_end вообще может быть действительным?

Имхо, это может сработать, если ctx->data_meta оставить без изменений, но тогда у меня проблема в пользовательском пространстве, потому что я не знаю, где находится data_meta. Есть ли какая-нибудь вспомогательная функция libbpf для получения доступа к метаданным пакета?

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


person binaryBigInt    schedule 02.03.2020    source источник
comment
Я могу неправильно понять, но при очень быстром взгляде я не понимаю, как вы могли писать в ctx->data - <something>, прежде чем сначала добавить запас, поскольку это было бы до начала пакетных данных и, следовательно, вне допустимого диапазона верно. Обратите внимание, что код, который вы использовали в качестве примера, описывается как «псевдокод», не обязательно что-то законченное/функциональное. Это помогает?   -  person Qeole    schedule 02.03.2020
comment
Я не знал о headroom - что это? Как я могу добавить это?   -  person binaryBigInt    schedule 02.03.2020
comment
См. этот помощник, который может использовать место до data, например для отправки новых заголовков инкапсуляции. Примечание. У меня не было времени подробно прочитать ваш вопрос, и я не знаю, нужно ли вам это вообще.   -  person Qeole    schedule 02.03.2020
comment
Я попробовал этот пример: github.com/xdp -project/net-next/blob/master/samples/bpf/, но bpf_xdp_adjust_meta всегда терпит неудачу (возвращает < 0). Я предполагаю, что мой драйвер устройства не поддерживает это?   -  person binaryBigInt    schedule 03.03.2020
comment
Вполне возможно. Помощник может возвращать -EACCES, -EINVAL или -ENOTSUPP. Последний случай указывает на то, что ваш драйвер не поддерживает метаданные XDP.   -  person Qeole    schedule 03.03.2020
comment
xdp_data_meta_unsupported проверяет наличие return unlikely(xdp->data_meta > xdp->data);. Значит, если data_meta больше xdp->data, эта операция не поддерживается?   -  person binaryBigInt    schedule 03.03.2020
comment
Ага. Проверьте функцию чуть выше, xdp_set_data_meta_invalid(), и ее комментарий: Drivers not supporting XDP metadata can use this helper, which rejects any room expansion for metadata as a result.   -  person Qeole    schedule 03.03.2020
comment
Я сбит с толку. Какой заголовок мне нужно импортировать, чтобы получить доступ к этой функции? :/   -  person binaryBigInt    schedule 03.03.2020
comment
Не уверен, какую функцию вы имеете в виду. xdp_data_meta_unsupported(), xdp_set_data_meta_invalid() — это внутренние функции ядра, последние, возможно, вызываются вашим драйвером сетевой карты, но не используются BPF в противном случае. Для bpf_xdp_adjust_meta() и других вспомогательных определений BPF вы можете использовать определения, как в tools/lib/bpf/bpf_helper_defs.h (номер 54 соответствует BPF_FUNC_xdp_adjust_meta в enum bpf_func_id в include/uapi/linux/bpf.h). Может быть, ваш драйвер просто не поддерживает метаданные XDP? Какой драйвер вы используете с вашей сетевой картой?   -  person Qeole    schedule 03.03.2020


Ответы (1)


Фрагмент, который вы использовали, описан в руководстве как псевдокод, это не полный и функциональный пример. Давайте посмотрим на журнал верификатора:

Load XDP program...
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
0: (bf) r6 = r1                 // r6 points to ctx
1: (61) r1 = *(u32 *)(r6 +16)   // [let's ignore that]
2: (63) *(u32 *)(r10 -4) = r1   // [and that]
3: (61) r7 = *(u32 *)(r6 +4)    // r7 points to ctx->data_end
4: (61) r8 = *(u32 *)(r6 +0)    // r8 points to ctx->data
5: (85) call bpf_ktime_get_ns#5 // r0 now contains timestamp
6: (bf) r1 = r8                 // r1 points to ctx->data
7: (07) r1 += -8                // r1 = data - sizeof(unsigned long long)
8: (2d) if r1 > r7 goto pc+3    // if(data-sizeof(unsigned long long)>data_end) jump;

Теперь самое интересное. Мы пытаемся сделать:

        ctx->data_meta = ctx->data - sizeof(unsigned long long);
        memcpy(&ctx->data_meta, &kstamp, sizeof(unsigned long long));

И производит:

 R0_w=inv(id=0) R1_w=pkt(id=0,off=-8,r=0,imm=0) R6_w=ctx(id=0,off=0,imm=0)
 R7_w=pkt_end(id=0,off=0,imm=0) R8_w=pkt(id=0,off=0,r=0,imm=0) R10=fp0 fp-8=mmmm????
9: (63) *(u32 *)(r6 +8) = r0
invalid bpf_context access off=8 size=4

r6 указывает на ctx типа struct xdp_md, поэтому r6 +8 равно ctx->data - sizeof(unsigned long long). Это связано с тем, что по умолчанию ctx->data_meta указывает на ctx->data. Поэтому, когда вы пытаетесь скопировать метку времени в ctx->data_meta, вы на самом деле пытаетесь записать ее за 8 байт до начала данных пакета. У вас нет специального места, зарезервированного для размещения метаданных, так что это внешний доступ: верификатор жалуется, что это недопустимо.

Но как же тогда мы можем выделить место для метаданных? Это делается благодаря bpf_xdp_adjust_meta() Помощник BPF, как в пример, на который вы ссылались.

Однако не все драйверы сетевых карт поддерживают метаданные XDP. Некоторые из них вызывают функцию xdp_set_data_meta_invalid() внутри, который устанавливает ctx->data_meta на ctx->data + 1 вместо ctx->data. Если bpf_xdp_adjust_meta(), вызванный из программы BPF, обнаружит, что эта настройка была выполнена, выдает ошибку с -ENOTSUPP и отказывается настраивать ctx->data_meta. Основываясь на обсуждении в комментариях, похоже, что это происходит в вашем случае.

Если у вас нет поддержки метаданных XDP, вы можете попытаться закодировать метку времени в другом поле вашего пакета. Карты BPF также могут быть вариантом, но вам потребуется одна запись на пакет, поэтому не уверен, что это повлияет на память/производительность.

person Qeole    schedule 03.03.2020