Использование физического адреса в качестве фрагмента данных 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);
Возможно, я ищу страницу по неправильному адресу или, возможно, она не существует. Может кто-то указать мне верное направление. Есть ли способ достичь цели без такого противного взлома?