GNU/Linux >> Belajar Linux >  >> Linux

Zero-copy ruang pengguna TCP kirim dari dma_mmap_coherent() memori yang dipetakan

Seperti yang saya posting di pembaruan dalam pertanyaan saya, masalah mendasarnya adalah jaringan zerocopy tidak berfungsi untuk memori yang telah dipetakan menggunakan remap_pfn_range() (yang dma_mmap_coherent() kebetulan juga digunakan di bawah tenda). Alasannya adalah jenis memori ini (dengan kode VM_PFNMAP flag set) tidak memiliki metadata berupa struct page* terkait dengan setiap halaman, yang dibutuhkannya.

Solusinya kemudian adalah mengalokasikan memori dengan cara struct page* s adalah terkait dengan memori.

Alur kerja yang sekarang berfungsi bagi saya untuk mengalokasikan memori adalah:

  1. Gunakan struct page* page = alloc_pages(GFP_USER, page_order); untuk mengalokasikan blok memori fisik yang berdekatan, di mana jumlah halaman yang berdekatan yang akan dialokasikan diberikan oleh 2**page_order .
  2. Pisahkan halaman orde tinggi/gabungan menjadi halaman orde 0 dengan memanggil split_page(page, page_order); . Ini sekarang berarti bahwa struct page* page telah menjadi larik dengan 2**page_order entri.

Sekarang untuk mengirimkan wilayah tersebut ke DMA (untuk penerimaan data):

  1. dma_addr = dma_map_page(dev, page, 0, length, DMA_FROM_DEVICE);
  2. dma_desc = dmaengine_prep_slave_single(dma_chan, dma_addr, length, DMA_DEV_TO_MEM, 0);
  3. dmaengine_submit(dma_desc);

Saat kami mendapat panggilan balik dari DMA bahwa transfer telah selesai, kami perlu membuka peta wilayah untuk mentransfer kepemilikan blok memori ini kembali ke CPU, yang menangani cache untuk memastikan kami tidak membaca data basi:

  1. dma_unmap_page(dev, dma_addr, length, DMA_FROM_DEVICE);

Sekarang, ketika kita ingin mengimplementasikan mmap() , yang harus kita lakukan hanyalah memanggil vm_insert_page() berulang kali untuk semua halaman 0 pesanan yang telah kami alokasikan sebelumnya:

static int my_mmap(struct file *file, struct vm_area_struct *vma) {
    int res;
...
    for (i = 0; i < 2**page_order; ++i) {
        if ((res = vm_insert_page(vma, vma->vm_start + i*PAGE_SIZE, &page[i])) < 0) {
            break;
        }
    }
    vma->vm_flags |= VM_LOCKED | VM_DONTCOPY | VM_DONTEXPAND | VM_DENYWRITE;
...
    return res;
}

Saat file ditutup, jangan lupa untuk membebaskan halaman:

for (i = 0; i < 2**page_order; ++i) {
    __free_page(&dev->shm[i].pages[i]);
}

Menerapkan mmap() cara ini sekarang memungkinkan soket untuk menggunakan buffer ini untuk sendmsg() dengan MSG_ZEROCOPY bendera.

Meskipun ini berhasil, ada dua hal yang tidak cocok dengan saya dengan pendekatan ini:

  • Anda hanya dapat mengalokasikan buffer berukuran power-of-2 dengan metode ini, meskipun Anda dapat mengimplementasikan logika untuk memanggil alloc_pages sebanyak yang dibutuhkan dengan urutan yang menurun untuk mendapatkan buffer ukuran apa pun yang terdiri dari sub-buffer dengan berbagai ukuran. Ini kemudian akan membutuhkan beberapa logika untuk mengikat buffer ini bersama-sama di mmap() dan untuk DMA mereka dengan scatter-gather (sg ) panggilan daripada single .
  • split_page() mengatakan dalam dokumentasinya:
 * Note: this is probably too low level an operation for use in drivers.
 * Please consult with lkml before using this in your driver.

Masalah-masalah ini akan mudah dipecahkan jika ada beberapa antarmuka di kernel untuk mengalokasikan jumlah halaman fisik yang bersebelahan secara sewenang-wenang. Saya tidak tahu mengapa tidak ada, tetapi menurut saya masalah di atas tidak begitu penting untuk menggali mengapa ini tidak tersedia / bagaimana menerapkannya :-)


Mungkin ini akan membantu Anda memahami mengapa halaman alokasi membutuhkan nomor halaman berkekuatan 2.

Untuk mengoptimalkan proses alokasi halaman (dan mengurangi fragmentasi eksternal), yang sering digunakan, kernel Linux mengembangkan cache halaman per-cpu dan pengalokasi sobat untuk mengalokasikan memori (ada pengalokasi lain, slab, untuk melayani alokasi memori yang lebih kecil dari halaman).

Cache halaman per-cpu melayani permintaan alokasi satu halaman, sedangkan buddy-allocator menyimpan 11 daftar, masing-masing berisi 2^{0-10} halaman fisik. Daftar ini bekerja dengan baik saat mengalokasikan dan membebaskan halaman, dan tentu saja, premisnya adalah Anda meminta buffer berukuran kekuatan 2.


Linux
  1. Manajemen Memori Linux – Swapping, Cache dan Shared VM

  2. Apa itu ioremap()

  3. Apa perbedaan antara menulis ke file dan memori yang dipetakan?

  1. Pengaruh SO_SNDBUF

  2. Cara yang lebih cepat untuk memindahkan halaman memori daripada mremap()?

  3. Apa itu halaman yang dipetakan memori dan halaman anonim?

  1. Penggunaan Memori Linux

  2. mmap:apakah file yang dipetakan akan segera dimuat ke memori?

  3. Jenkins aktif (keluar)