Можно ли отобразить физический адрес как фрагмент данных в 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
ed страницу на:
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
. Хотя он работает нормально, я действительно сомневаюсь, что это правильный способ сделать это. Тем не менее, это показывает, что нет аппаратных ограничений для этого. Кто-нибудь знает, как правильно отобразить эту память?
P.S. Я также пытался сопоставить физический адрес с фиксированным виртуальным адресом таким образом, чтобы 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);
Возможно, я ищу страницу по неправильному адресу или, возможно, она не существует.
Может кто-то указать мне верное направление. Есть ли способ достичь цели без такого противного взлома?