Использование физического адреса в качестве фрагмента данных sk_buff - PullRequest
0 голосов
/ 16 ноября 2018

Можно ли отобразить физический адрес как фрагмент данных в 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);

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

...