2011-11-01 9 views
5

Piszę sterownik jądra Linuksa o niestandardowe urządzenie USB, które będzie używać luźnych punktów końcowych, wszystko wydaje się działać dobrze, jednak otrzymuję bardzo wolne szybkości transmisji danych. W szczególności zajmuje około 25 sekund na zapisanie i odczytanie danych o wartości 10 MB. Próbowałem tego w systemie wbudowanym i maszynie wirtualnej Linux działającej na rozsądnym komputerze z podobnymi wynikami.bardzo słaba wydajność (~ 0,4MB/s) z linuksowym ładowaniem jądra USB i przeniesieniem jądra sprzętowego

Używam zestawu rozwojowego EZ-USB FX2 z Cypress jako planszy docelowej. To działa oprogramowanie układowe, które ustawia dwa i dwa końcowe punkty końcowe. Każdy punkt końcowy jest podwójnie buforowany i obsługuje 512-bajtowe okna. Oprogramowanie układa sondę końcową przez pętlę while (1) w trybie main(), bez uśpienia i kopiuje dane z punktu końcowego do punktu końcowego, gdy dane te są dostępne za pomocą operatorów automatycznych. Powiedziano mi, że może to przenosić dane w sposób uczciwy w systemie Windows przy użyciu ich konkretnych aplikacji, ale nie miałem okazji zweryfikować tego.

Mój kod (odpowiednie fragmenty poniżej) wywołuje funkcję o nazwie bulk_io w procedurze sondy urządzenia. Ta funkcja tworzy liczbę (URB_SETS) spośród urb, które próbują zapisać 512 bajtów na urządzeniu. Zmiana tej liczby z zakresu od 1 do 32 nie zmienia wydajności. Wszystkie kopiują z tego samego bufora. Procedura obsługi wywołania zwrotnego dla każdej operacji zapisu do punktu końcowego jest używana do utworzenia adresu URL odczytu na odpowiednim punkcie końcowym. Oddzwaniane wywołanie tworzy kolejne urb do zapisu, dopóki nie osiągnę całkowitej liczby żądań zapisu/odczytu, które chcę uruchomić w tym samym czasie (20 000). Pracuję teraz, aby większość operacji w funkcjach wywołania zwrotnego przenieść na dolne połówki w przypadku, gdy blokują one inne przerwania. Zastanawiam się również nad przepisaniem oprogramowania układowego pętli zbiorczej dla Cypress FX2, aby używał przerwania zamiast odpytywania. Czy jest tu coś, co wygląda na nietypowe, aby wydajność była tak niska? Z góry dziękuję. Daj mi znać, jeśli chcesz zobaczyć więcej kodu, to po prostu niezaprogramowany sterownik do testowania operacji wejścia/wyjścia na Cypress FX2.

To się końcowy funkcja zapisu zwrotna:

static void bulk_io_out_callback0(struct urb *t_urb) { 
    // will need to make this work with bottom half 
    struct usb_dev_stat *uds = t_urb->context; 
    struct urb *urb0 = usb_alloc_urb(0,GFP_KERNEL); 
    if (urb0 == NULL) { 
      printk("bulk_io_out_callback0: out of memory!"); 
    } 
    usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_rcvbulkpipe(uds->udev,uds->ep_in[0]), uds->buf_in, uds->max_packet, bulk_io_in_callback0, uds); 
    usb_submit_urb(urb0,GFP_KERNEL); 
    usb_free_urb(urb0); 
} 

Jest to w końcowym odczytać funkcję callback:

static void bulk_io_in_callback0(struct urb *t_urb) { 
    struct usb_dev_stat *uds = t_urb->context; 

    struct urb *urb0 = usb_alloc_urb(0,GFP_KERNEL); 
    if (urb0 == NULL) { 
      printk("bulk_io_out_callback0: out of memory!"); 
    } 

    if (uds->seq--) { 
      usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_sndbulkpipe(uds->udev,uds->ep_out[0]), uds->buf_out, uds->max_packet, bulk_io_out_callback0, uds); 
      usb_submit_urb(urb0,GFP_KERNEL); 
    } 
    else { 
      uds->t1 = get_seconds(); 
      uds->buf_in[9] = 0; // to ensure we only print the first 8 chars below 
      printk("bulk_io_in_callback0: completed, time=%lds, bytes=%d, data=%s\n", (uds->t1-uds->t0), uds->max_packet*SEQ, uds->buf_in); 
    } 
    usb_free_urb(urb0); 
} 

Ta funkcja jest wywoływana skonfigurować początkowe Urbs:

static int bulk_io (struct usb_interface *interface, struct usb_dev_stat *uds) { 
    struct urb *urb0; 
    int i; 

    uds->t0 = get_seconds(); 

    memcpy(uds->buf_out,"abcd1234",8); 

    uds->seq = SEQ; // how many times we will run this 

    printk("bulk_io: starting up the stream, seq=%ld\n", uds->seq); 

    for (i = 0; i < URB_SETS; i++) { 
      urb0 = usb_alloc_urb(0,GFP_KERNEL); 
      if (urb0 == NULL) { 
        printk("bulk_io: out of memory!\n"); 
        return(-1); 
      } 

      usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_sndbulkpipe(uds->udev,uds->ep_out[0]), uds->buf_out, uds->max_packet, bulk_io_out_callback0, uds); 
          printk("bulk_io: submitted urb, status=%d\n", usb_submit_urb(urb0,GFP_KERNEL)); 
      usb_free_urb(urb0); // we don't need this anymore 
    } 


    return(0); 
} 

Edytuj 1 Weryfikuję ed że udev-> prędkość == 3, więc USB_SPEED_HIGH, czyli to nie jest, ponieważ Linux uważa, że ​​to powolne urządzenie ....

Edycja 2 przeniosłem wszystko w wywołań zwrotnych związanych z tworzeniem URB (kmalloc, przesłać) i uwolnienie do dolnej połowy, tej samej wydajności.

+3

Tajemnica już nie istnieje. Zmodyfikowałem oprogramowanie "bulkloop" CY7C68013A, aby przełączać GPIO, gdy przesuwa on dane/uzbrajające punkty końcowe i spędzało ~ 80% swoich cykli, wykonując tę ​​funkcję. Wygląda na to, że rdzeń 8051 dotyka buforów USB ze wszystkimi ograniczeniami przepustowości do ~ 0.5MB/s, jak pokazano powyżej. Poszedłem do przodu i testowałem ich demo bulkloop z biblioteki CyUSB lib i osiągałem znacznie gorszą wydajność, około 0.1MB/s. Podsumowując, korzystanie z oprogramowania układowego luzem nie jest dobrym testem wydajności sterownika USB. Spróbuję go z FPGA dostarczającym dane CY7C68013A. – armguy

+0

tylko mały punkt tutaj, powinieneś utrzymywać callbacki URB tak małe, jak to możliwe (np. Po prostu ustawienie flagi) –

Odpowiedz

1

Czytanie i pisanie w małych fragmentach jest z mojego doświadczenia mało skuteczne.

Używam zestawu rozwojowego EZ-USB FX2 z Cypress jako planszy docelowej. To działa oprogramowanie układowe, które ustawia dwa i dwa końcowe punkty końcowe. Każdy punkt końcowy jest podwójnie buforowany i obsługuje 512-bajtowe okna.

Nie oznacza to, że można zapisać do niego nie więcej niż 512 bajtów naraz.

Spróbowałbym napisać do niego nie mniej niż 4096 bajtów, ponieważ jest to standardowy rozmiar strony (być może nie tak standardowy w systemach wbudowanych).Gdyby to zadziałało, spróbowałbym napisać do 1/4 megabajta na raz, a nawet więcej, gdyby to działało.

Kluczową sprawą jest ustalenie, kiedy okno zapisu urządzenia jest pełne. Gdy tak się stanie, wywoła wszelkie wywołania zwrotne lub otrzyma te informacje w inny sposób i użyje go do sygnalizowania zgłoszenia, aby przestał pisać.

Należy zauważyć, że okno nie będzie pełne po "nadaniu urządzeniu" 512 bajtów, ponieważ urządzenie zacznie czytać z tego okna, gdy tylko będzie cokolwiek do przeczytania.

Być może przegapiłem coś ważnego w twoim pytaniu, ale to, co mówię, to zasadniczo, że musisz napisać więcej niż 512 bajtów na raz. Właśnie dlatego masz tak słabą wydajność.