Зачем нам нужен пул DMA? - PullRequest
0 голосов
/ 07 марта 2020

Я читаю https://www.kernel.org/doc/Documentation/DMA-API.txt и не понимаю, зачем нужен пул DMA.

Почему бы не выделить PAGE_SIZE DMA память dma_alloc_coherent и использовать смещения?

Кроме того, почему dynamici c DMA полезен для драйвера сетевого устройства вместо повторного использования той же памяти DMA?

Что является наиболее производительным для передачи данных <1 КБ? </p>

1 Ответ

1 голос
/ 08 марта 2020

Предупреждение: я не специалист по linux ядру.

Книга LDD (которая может быть лучше читать для начала) говорит, что пул DMA лучше работает для небольших областей dma ( короче страницы) - https://static.lwn.net/images/pdf/LDD3/ch15.pdf стр. 447 или https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch15.html, раздел «Пулы DMA»:

Пул DMA - это механизм выделения для небольшие, согласованные отображения DMA. Отображения, полученные из dma_alloc_coherent, могут иметь минимальный размер одной страницы. Если вашему устройству требуются области DMA меньшего размера, возможно, вам следует использовать пул DMA. Пулы DMA также полезны в ситуациях, когда у вас может возникнуть желание выполнить DMA для небольших областей, встроенных в большую структуру. Некоторые очень неясные ошибки драйвера были отнесены к проблемам когерентности кэша с полями структуры, смежными с небольшими областями прямого доступа к памяти. Чтобы избежать этой проблемы, вы всегда должны явно выделять области для операций DMA, вдали от других структур данных, не относящихся к DMA. ... Распределения обрабатываются с помощью dma_pool_alloc

То же самое указано в https://www.kernel.org/doc/Documentation/DMA-API-HOWTO.txt

Если вашему драйверу требуется много меньших областей памяти Вы можете написать собственный код для разделения страниц, возвращаемых dma_alloc_coherent (), или вы можете использовать API dma_pool для этого. Dma_pool похож на kmem_cache, но он использует dma_alloc_coherent (), а не __get_free_pages (). Кроме того, он понимает общие аппаратные ограничения для выравнивания, такие как заголовки очередей, которые необходимо выравнивать по границам байтов N.

Таким образом, пулы DMA являются оптимизацией для меньших распределений. Вы можете использовать dma_alloc_coherent для каждой маленькой памяти dma индивидуально (с большими издержками) или вы можете попытаться создать свой собственный пул (больше пользовательского кода для управления смещениями и распределениями). Но пулы DMA уже реализованы, и их можно использовать.

Производительность методов должна быть профилирована для вашего случая.

Пример динамической c регистрации dma в сетевом драйвере (используется для фрагментов skb ): https://elixir.bootlin.com/linux/v4.6/source/drivers/net/ethernet/realtek/r8169.c

static struct sk_buff *rtl8169_alloc_rx_data
    mapping = dma_map_single(d, rtl8169_align(data), rx_buf_sz,
                 DMA_FROM_DEVICE);
static int rtl8169_xmit_frags
        mapping = dma_map_single(d, addr, len, DMA_TO_DEVICE);
static netdev_tx_t rtl8169_start_xmit
    mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE);
static void rtl8169_unmap_tx_skb
    dma_unmap_single(d, le64_to_cpu(desc->addr), len, DMA_TO_DEVICE);

Регистрация фрагментов skb для dma на месте может быть лучше (если sg dma поддерживается NI C чип), чем копировать каждый фрагмент из skb в некоторую память DMA. Обратитесь к книге «Понимание Linux Сетевые компоненты» для раздела «Функция dev_queue_xmit» и Глава 21; и skb_linearize

Пример использования пула DMA - драйвер nvme (prp равен часть элемента очереди передачи , страницы физического региона, 64-битный указатель, и «Список PRP содержит список PRP, как правило, без смещений. "):

https://elixir.bootlin.com/linux/v4.6/source/drivers/nvme/host/pci.c#L1807

static int nvme_setup_prp_pools(struct nvme_dev *dev)
{
    dev->prp_page_pool = dma_pool_create("prp list page", dev->dev,
                        PAGE_SIZE, PAGE_SIZE, 0);

static bool nvme_setup_prps
    prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma);

static void nvme_free_iod
        dma_pool_free(dev->prp_small_pool, list[0], prp_dma);
...