2013-10-18 33 views
8

Próbuję przydzielić bufor DMA dla obciążenia HPC. Wymaga 64 GB przestrzeni buforowej. W międzyczasie niektóre dane są odciążane na kartę PCIe. Zamiast kopiować dane do garści dinky 4 MB buforów podanych przez pci_alloc_consistent, chciałbym po prostu utworzyć 64 bufory 1GB, wspierane przez 1GB HugePages.Jak przydzielić bufor DMA wspierany przez 1GB HugePages w module jądra Linux?

Niektóre informacje tła: wersja jądra: CentOS 6.4/2.6.32-358.el6.x86_64 opcji bootowania: hugepagesz = 1g hugepages = 64 default_hugepagesz = 1g

odpowiednią porcję/proc/meminfo: AnonHugePages: 0 kB HugePages_Total HugePages_Free: 64: 64: 0 HugePages_Rsvd HugePages_Surp 0 Hugepagesize: 1048576 kB DirectMap4k: 848 kB DirectMap2M: 2062336 kB DirectMap1G: 132120576 kB

Mogę zamontować -t hugetlbfs nodev/mnt/hugepages. CONFIG_HUGETLB_PAGE jest prawdziwe. MAP_HUGETLB jest zdefiniowany.

Przeczytałem kilka informacji o używaniu libhugetlbfs do wywoływania get_huge_pages() w przestrzeni użytkownika, ale najlepiej ten bufor zostałby przydzielony w przestrzeni jądra. Próbowałem wywoływać do_mmap() z MAP_HUGETLB, ale wydawało się, że nie zmienia to liczby bezpłatnych stron wścibskich, więc nie sądzę, że faktycznie wspierało mmap dużymi stronami.

Więc domyślam się, do czego dążę, czy jest tam jakikolwiek sposób mogę mapować bufor do 1GB HugePage w przestrzeni jądra, czy to musi być zrobione w przestrzeni użytkownika? Lub jeśli ktoś wie w inny sposób, mogę uzyskać ogromną (1-64 GB) ilość ciągłej pamięci fizycznej dostępnej jako bufor jądra?

+0

Czy Twoim celem jest przede wszystkim uniknięcie kopiowania między jądrem a przestrzenią użytkownika? – ChuckCottrill

+2

Wszystkie te interfejsy API dotyczą obszaru użytkownika. Zobacz, jak zaimplementowano hugetlbfs, szczególnie 'hugetlbfs_file_mmap'. –

+0

@muusbolla Czy możesz znaleźć odpowiedź? –

Odpowiedz

1

PROBLEM

  1. Zwykle, jeśli chcesz przydzielić bufora DMA, lub uzyskać adres fizyczny, odbywa się to w przestrzeni jądra, jak kod użytkownika nie powinno się syf wokół adresów fizycznych.
  2. Hugetlbfs zapewnia tylko mapowania dla użytkownika przestrzeń przeznaczyć 1GB ogromne stron i uzyskać łatwy w przestrzeni adresów wirtualnych
  3. No funkcja istnieje mapować hugepage użytkownik wirtualny adres fizyczny adres

EUREKA

Ale funkcja istnieje! Pochowany deep in the 2.6 kernel source code leży tę funkcję, aby uzyskać stronę struct z wirtualnego adresu, oznaczone jako „tylko do testowania” i blokowano #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; 
} 

ROZWIĄZANIE: Ponieważ funkcja powyżej nie jest faktycznie wkompoliwany jądro, będziesz musiał dodać go do swojego źródła sterownika.

USER SIDE WORKFLOW

  1. Przeznaczyć 1GB hugepages przy starcie z opcji bootowania
  2. połączeń get_huge_pages() z hugetlbfs uzyskać wskaźnik przestrzeni użytkownika (wirtualny adres)
  3. Przełęcz użytkownika wirtualny adres (normalny wskaźnik oddanych do unsigned long) do ioctl kierowcy

KERNEL DRIVER WORKFLOW

  1. Zebrane użytkownikowi wirtualny adres za pośrednictwem ioctl follow_huge_addr
  2. Zadzwoń aby uzyskać stronę struct *
  3. połączeń page_to_phys na stronie struct *, aby uzyskać adres fizyczny
  4. Podaj adres fizyczny do urządzenia za DMA
  5. połączenia kmap na stronie struct * jeśli chcesz jądro wirtualny wskaźnik

ZASTRZEŻENIE

  • Powyższe kroki są zbierane kilka lat później. Utraciłem dostęp do oryginalnego kodu źródłowego. Wykonaj należytą staranność i upewnij się, że nie zapomnę kroku.
  • Jedynym powodem, dla którego to działa, jest to, że przy rozruchu przydzielane są ogromne strony 1GB, a ich fizyczne adresy są trwale zablokowane. Nie próbuj mapować adresu wirtualnego użytkownika, który nie zawiera 1GBhug, na adres fizyczny DMA! Będziesz mieć zły czas!
  • Należy dokładnie przetestować system, aby potwierdzić, że ogromne strony o pojemności 1 GB są w rzeczywistości zablokowane w pamięci fizycznej i że wszystko działa dokładnie. Ten kod działał bezbłędnie na mojej konfiguracji, ale tutaj jest wielkie niebezpieczeństwo, jeśli coś pójdzie nie tak.
  • Ten kod jest gwarantowany tylko w architekturze x86/x64 (gdzie adres fizyczny == adres magistrali) i na wersji jądra 2.6.XX. Może to być łatwiejszy sposób na późniejsze wersje jądra lub może być teraz zupełnie niemożliwe.
2

Nie jest to często wykonywane w przestrzeni jądra, więc nie za dużo przykładów.

Podobnie jak inne strony, ogromne stron są przydzielane z alloc_pages, do melodii:

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

HPAGE_PMD_ORDER jest makro, określając kolejność jednym ogromnym stronie w kategoriach normalnych stron. Powyższe oznacza, że ​​przezroczyste ogromne strony są włączone w jądrze.

Następnie można kontynuować mapowanie uzyskanego wskaźnika strony w zwykły sposób za pomocą kmap().

Nota prawna: Sam nigdy tego nie próbowałem, więc być może trzeba będzie poeksperymentować. Jedną z rzeczy do sprawdzenia jest: HPAGE_PMD_SHIFT reprezentuje kolejność mniejszej "ogromnej" strony. Jeśli chcesz użyć tych gigantycznych stron o pojemności 1GB, prawdopodobnie będziesz musiał wypróbować inną kolejność, prawdopodobnie PUD_SHIFT - PAGE_SHIFT.

+1

Czy są przezroczyste ogromne strony dla stron o pojemności 1 GB? – osgx