AF-XDP: возможно ли параллельное чтение из Shared UMEM (но запись последовательно)? - PullRequest
0 голосов
/ 22 марта 2020

Мне жаль, что я задаю все эти вопросы XDP, но там действительно не так много документации.

Я посмотрел на файл xdpsock_user.c (https://github.com/torvalds/linux/blob/master/samples/bpf/xdpsock_user.c) в котором реализован Shared Umem вполне понятно с несколькими сокетами. В for-l oop каждый сокет будет запрашиваться путем вызова xsk_ring_cons__peek, если какие-либо пакеты поступили. Если их нет, for-l oop будет go до следующего сокета. В противном случае программа обработает полученные пакеты.

Как видите, это поведение последовательно (один сокет за другим). Я не знаю, можно ли это изменить на Multithreaded-Architecture, где потоки (каждый со своим собственным сокетом) читают параллельно из Umem, например:

uint32_t idx_rx = 0, idx_fq = 0;

const int rcvd = xsk_ring_cons__peek(&xsk_socket->rx, INT32_MAX, &idx_rx);
if (!rcvd) {
    return;
}

const int ret = xsk_ring_prod__reserve(&xsk_socket->umem->fq, rcvd, &idx_fq);
if (ret < 0) {
    fprintf(stderr, "Error: %s\n", strerror(-ret));
} else if(ret == 0) {
    printf("NO SPACE LEFT!\n");
} else if(ret != rcvd) {
    printf("RET != RCVD\n");
} else {
    for (uint32_t i = idx_rx; i < (idx_rx + rcvd); i++) {
        const struct xdp_desc *desc = xsk_ring_cons__rx_desc(&xsk_socket->rx, i);
        uint64_t addr = desc->addr;
        uint32_t len = desc->len;
        uint64_t orig = xsk_umem__extract_addr(addr);

        addr = xsk_umem__add_offset_to_addr(addr);

        const int hdr_size = process_packet(xsk_socket, addr, len);
        *xsk_ring_prod__fill_addr(&xsk_socket->umem->fq, idx_fq++) = orig;

        xsk_socket->stats.rx_bytes += (len - hdr_size);
    }

    xsk_socket->stats.rx_packets += rcvd;
}

Но вызывая

    xsk_ring_prod__submit(&xsk_socket->umem->fq, ret);
    xsk_ring_cons__release(&xsk_socket->rx, rcvd);

будет происходить последовательно (каждый поток может поместить запрос в release и submit вместе со своими xsk_socket->rx и полученной суммой в потокобезопасную очередь сообщений, и другой поток ничего не будет делать, кроме выполнения этих двух функций для каждого входящего сообщения).

Я реализовал эту архитектуру и, хотя она отлично работает для одного сокета в очереди RX (поэтому нет общего Umem и только один поток), я получаю большую потерю пакетов для два сокета, которые совместно используют Umem.

Итак, я предполагаю, что это невозможно, поскольку Umem является очередью с одним потребителем или одним производителем?

...