Использование физического адреса в качестве фрагмента данных sk_buff

Можно ли отобразить физический адрес как фрагмент данных в sk_buff? Я работаю на платформе Zynq Ultrascale+ (FPGA + ARM SOC). У меня есть буфер памяти сопоставлен с физическим адресом. Цель состоит в том, чтобы эффективно отправить эти данные по UDP. Под эффективностью я подразумеваю ZEROCOPY. Я пытаюсь разработать драйвер Linux, который отобразит этот физический адрес в память ядра и добавит его в sk_buff как фрагмент Я начал с:

#define PACKET_LEN 1024
struct page *pag;

struct net_device *dev;
struct sk_buff *skb = NULL;
skb = alloc_skb(LL_RESERVED_SPACE(dev) + PACKET_LEN + ip_header_l +
                udp_header_l, GFP_ATOMIC);
udp = skb_push(skb, udp_header_l);

//Fill up udp header
...

ip = skb_push(skb, ip_header_l);

//fill up ip header
...

dev_hard_header(skb, dev, ETH_P_IP, addr, myaddr, dev->addr_len);
skb->dev = dev;
//map page with data as fragment
skb_fill_page_desc(skb, 0, pag, 0, PACKET_LEN);
//send data
dev_queue_xmit(skb);

И пока страница создается:

pagebuff = vmalloc(PACKET_LEN);
pag = vmalloc_to_page(pagebuff);

Все отлично работает. Пакет отправляется. Пакет отправляется двумя транзакциями DMA (Scatter Gather). Идя к своей цели, я заменил vmalloc редактировать страницу с:

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
membase = devm_ioremap_resource(&pdev->dev, res);
pag = virt_to_page(membase);

Физический адрес 0xb0000000 и сопоставлен с виртуальным адресом 0xffffff800ad30000 страница находится на 0xffffffbf0025e280, После dev_queue_xmit пакет отправляется в сетевую очередь и заканчивается отображением для DMA. Проблема возникает когда swiotlb_map_page использования 0x00ad30000 как phys_addr, который отличается от оригинала 0xb0000000, virt_to_phys используется в swiotlb_map_page чтобы вычислить физический адрес, и он в основном принимает младшие 32 бита в качестве физического адреса. Есть ли другой способ сопоставить область памяти, чтобы его можно было использовать как sk_buff фрагмент?

В качестве временного исправления я создал поддельную страницу следующим образом:

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pag = alloc_page(0); //create fake page
memset(pag, 0, sizeof(struct page));
pag->private = res->start;

И пропатченный драйвер Ethernet для использования личных данных страницы в качестве адреса отображения:

mapping = skb_frag_page(frag)->private;
if (mapping) {
    //  printk("macb mapping override to %p\n",mapping);
}
else {
    mapping = skb_frag_dma_map(&bp->pdev->dev, frag, offset, size, DMA_TO_DEVICE);
    if (dma_mapping_error(&bp->pdev->dev, mapping))
        goto dma_error;
}

С таким взломом все работает. Данные заполнены содержанием 0xb0000000, Хотя он работает нормально, я действительно сомневаюсь, что это правильный способ сделать это. Тем не менее, это показывает, что нет аппаратных ограничений для этого. Кто-нибудь знает, как правильно отобразить эту память?

PS Я также пытался сопоставить физический адрес с фиксированным виртуальным адресом таким образом, чтобы swiotlb_map_page будет рассчитывать правильный адрес (и virt_to_phys сделал), но это закончилось ошибкой "Невозможно обработать пейджинговый запрос ядра по виртуальному адресу".

membase = phys_to_virt(res->start);
i = ioremap_page_range(membase, membase + resource_size(res),
                       res->start, PAGE_KERNEL);
//I tried both
pag = phys_to_page(res->start);
pag = virt_to_page(membase);

Возможно, я ищу страницу по неправильному адресу или, возможно, она не существует. Может кто-то указать мне верное направление. Есть ли способ достичь цели без такого противного взлома?

0 ответов

Другие вопросы по тегам