GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana cara mengalokasikan buffer DMA yang didukung oleh HugePages 1GB dalam modul kernel linux?

MASALAH

  1. Biasanya jika Anda ingin mengalokasikan buffer DMA, atau mendapatkan alamat fisik, ini dilakukan di ruang kernel, karena kode pengguna tidak boleh dipusingkan dengan alamat fisik.
  2. Hugetlbfs hanya menyediakan pemetaan ruang pengguna untuk mengalokasikan 1 GB halaman besar, dan mendapatkan alamat virtual ruang pengguna
  3. Tidak ada fungsi untuk memetakan alamat virtual halaman besar pengguna ke alamat fisik

EUREKA

Tapi fungsinya memang ada! Terkubur jauh di dalam kode sumber kernel 2.6 terdapat fungsi ini untuk mendapatkan halaman struct dari alamat virtual, ditandai sebagai "hanya untuk pengujian" dan diblokir dengan #if 0:

#if 0   /* This is just for testing */
struct page *
follow_huge_addr(struct mm_struct *mm, unsigned long address, int write)
{
    unsigned long start = address;
    int length = 1;
    int nr;
    struct page *page;
    struct vm_area_struct *vma;

    vma = find_vma(mm, addr);
    if (!vma || !is_vm_hugetlb_page(vma))
        return ERR_PTR(-EINVAL);

    pte = huge_pte_offset(mm, address);

    /* hugetlb should be locked, and hence, prefaulted */
    WARN_ON(!pte || pte_none(*pte));

    page = &pte_page(*pte)[vpfn % (HPAGE_SIZE/PAGE_SIZE)];

    WARN_ON(!PageHead(page));

    return page;
}

SOLUSI:Karena fungsi di atas sebenarnya tidak dikompilasi ke dalam kernel, Anda perlu menambahkannya ke sumber driver Anda.

Alur KERJA SISI PENGGUNA

  1. Alokasikan halaman besar 1gb saat boot dengan opsi boot kernel
  2. Panggil get_huge_pages() dengan hugetlbfs untuk mendapatkan penunjuk ruang pengguna (alamat virtual)
  3. Teruskan alamat virtual pengguna (penunjuk normal dilemparkan ke panjang yang tidak ditandatangani) ke driver ioctl

Alur KERJA DRIVER KERNEL

  1. Terima alamat virtual pengguna melalui ioctl
  2. Panggil follow_huge_addr untuk mendapatkan halaman struct*
  3. Panggil page_to_phys pada halaman struct* untuk mendapatkan alamat fisik
  4. Berikan alamat fisik ke perangkat untuk DMA
  5. Panggil kmap pada halaman struct* jika Anda juga menginginkan pointer virtual kernel

PENAFIAN

  • Langkah-langkah di atas diingat kembali beberapa tahun kemudian. Saya kehilangan akses ke kode sumber asli. Lakukan uji tuntas dan pastikan saya tidak melupakan satu langkah pun.
  • Satu-satunya alasan ini berhasil adalah karena halaman berukuran besar 1 GB dialokasikan saat boot dan alamat fisiknya dikunci secara permanen. Jangan coba memetakan alamat virtual pengguna yang tidak didukung halaman besar ke alamat fisik DMA! Anda akan mengalami waktu yang buruk!
  • Uji dengan hati-hati pada sistem Anda untuk mengonfirmasi bahwa halaman besar 1GB Anda sebenarnya terkunci dalam memori fisik dan semuanya berfungsi dengan baik. Kode ini bekerja dengan sempurna pada penyiapan saya, tetapi ada bahaya besar di sini jika terjadi kesalahan.
  • Kode ini hanya dijamin bekerja pada arsitektur x86/x64 (dengan alamat fisik ==alamat bus), dan pada kernel versi 2.6.XX. Mungkin ada cara yang lebih mudah untuk melakukan ini pada versi kernel yang lebih baru, atau mungkin sama sekali tidak mungkin sekarang.

Ini tidak umum dilakukan di ruang kernel, jadi tidak terlalu banyak contoh.

Sama seperti halaman lainnya, halaman besar dialokasikan dengan alloc_pages, sesuai irama:

struct page *p = alloc_pages(GFP_TRANSHUGE, HPAGE_PMD_ORDER);

HPAGE_PMD_ORDER adalah makro, yang menentukan urutan satu halaman besar dalam hal halaman normal. Di atas menyiratkan bahwa halaman besar transparan diaktifkan di kernel.

Kemudian Anda dapat melanjutkan memetakan penunjuk halaman yang diperoleh dengan cara biasa dengan kmap().

Penafian:Saya belum pernah mencobanya sendiri, jadi Anda mungkin harus melakukan beberapa percobaan. Satu hal yang perlu diperiksa adalah ini:HPAGE_PMD_SHIFT mewakili urutan halaman "besar" yang lebih kecil. Jika Anda ingin menggunakan halaman raksasa 1 GB itu, Anda mungkin perlu mencoba urutan lain, mungkin PUD_SHIFT - PAGE_SHIFT.


Linux
  1. Cara memutakhirkan Kernel di Desktop Linux

  2. Linux – Kernel Tercemar Di Linux?

  3. Cara Membuat, Mengkompilasi, Memuat Modul Kernel yang Dapat Dimuat LKM Linux

  1. Cara Menulis Modul Kernel Linux Anda Sendiri dengan Contoh Sederhana

  2. Bagaimana cara membuat kode modul kernel Linux?

  3. Cara mengatur callback pengatur waktu berkala dalam modul kernel Linux

  1. Linux – Bagaimana Cara Menentukan Modul Yang Menodai Kernel?

  2. Linux – Bagaimana Cara Memuat Ulang Modul Kernel dengan Benar?

  3. Bagaimana cara memetakan buffer kernel Linux ke ruang pengguna?