2011-11-01 5 views
5

Sto scrivendo un driver del kernel Linux per un dispositivo USB personalizzata che utilizzerà gli endpoint di massa, tutto sembra funzionare bene, però, ho sto ottenendo dati molto lenti. In particolare, ci vogliono circa 25 secondi per scrivere e leggere 10 MB di dati. Ho provato questo su un sistema embedded e una VM Linux in esecuzione su un PC ragionevole con risultati simili.molto scarso rendimento (~ 0.4MB/s) con Linux massa USB driver del kernel di trasferimento e l'hardware di loopback

Sto usando un kit di sviluppo FX2 EZ-USB da Cypress come il bordo di destinazione. È in esecuzione il firmware del bulkloop che imposta due endpoint in e due out. Ogni endpoint ha un doppio buffer e supporta finestre a 512 byte. I sondaggi firmware fuori endpoint tramite un po '(1) loop in main(), senza dormire, e copia i dati da fuori al endpoint quando tali dati sono disponibili utilizzando autopointers. Mi è stato detto che questo può spostare i dati abbastanza su Windows utilizzando la loro applicazione specifica ma non hanno avuto la possibilità di verificarlo.

Il mio codice (porzioni rilevanti sotto) chiama una funzione chiamata bulk_io nella routine della sonda dispositivo. Questa funzione crea un numero (URB_SETS) di out urbs che tenta di scrivere 512 byte sul dispositivo. La modifica di questo numero tra 1 e 32 non modifica le prestazioni. Stanno tutti copiando dallo stesso buffer. Il gestore di callback per ciascuna operazione di scrittura su un endpoint esterno viene utilizzato per creare una lettura urb sull'endpoint corrispondente. Il callback letto crea un'altra scrittura fino a quando non ho raggiunto il numero totale di richieste di scrittura/lettura che voglio eseguire contemporaneamente (20.000). Sto lavorando ora per trasferire la maggior parte delle operazioni nelle funzioni di callback nelle metà inferiori nel caso in cui stiano bloccando altri interrupt. Sto anche pensando di riscrivere il firmware bulk-loop per Cypress FX2 per utilizzare gli interrupt anziché il polling. C'è qualcosa qui che sembra fuori dall'ordinario per rendere la performance così bassa? Grazie in anticipo. Per favore fatemi sapere se volete vedere più codice, questo è solo un driver bare-bone per testare l'I/O sul Cypress FX2.

Questo è il out endpoint funzione di callback scrittura:

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); 
} 

Questo è il nel endpoint funzione di callback leggere:

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); 
} 

Questa funzione viene chiamata per impostare i urbs iniziali:

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); 
} 

Edit 1 I verifi Ed che udev-> velocità == 3, così USB_SPEED_HIGH, il che significa questo non è perché Linux pensa che questo è un dispositivo lento ....

Edit 2 ho spostato tutto nelle callback relative alla creazione urb (kmalloc, inviare) e liberare nelle metà inferiori, stessa prestazione.

+3

Quindi il mistero non c'è più. Ho modificato il firmware "bulkloop" del CY7C68013A per attivare o disattivare un GPIO quando si spostano i punti finali di inserimento/inserimento dati e ha trascorso circa l'80% dei suoi cicli facendo questa funzione. Sembra che avere il core 8051 tocca i buffer USB per ridurre il throughput a ~ 0,5 MB/s come mostrato sopra. Sono andato avanti e confrontato con la loro dimostrazione di Bulloop lib windows CyUSB e ho ottenuto prestazioni molto peggiori, circa 0,1 MB/s. In conclusione, l'utilizzo del firmware del bulkloop non è un buon test delle prestazioni del driver USB. Lo proveremo con un FPGA che alimenterà i dati del CY7C68013A. – armguy

+0

solo un piccolo punto qui, dovresti mantenere le callback URB il più piccolo possibile (ad esempio, basta impostare una bandiera) –

risposta

1

Lettura e scrittura in piccoli pezzi è nella mia esperienza non molto efficace.

Sto utilizzando un kit di sviluppo EZ-USB FX2 di Cypress come scheda di destinazione. Sta eseguendo il firmware del bulkloop che imposta due endpoint in e due out. Ogni endpoint ha un doppio buffer e supporta finestre a 512 byte.

Questo non significa che è possibile non scrivere più di 512 byte alla volta.

Vorrei provare a scrivere non meno di 4096 byte alla volta, perché quella è la dimensione delle pagine standard (forse non così standard nei sistemi embedded).Se funzionasse, proverei a scrivere fino a 1/4 di un megabyte alla volta, e poi ancora di più se ciò funzionasse.

Il punto chiave qui è sapere quando la finestra di scrittura del dispositivo è piena. Quando lo è, chiamerà tutte le richiamate o le otterrete con qualsiasi altro mezzo e le userà per segnalare alla vostra applicazione di smettere di scrivere.

Si noti che la finestra non sarà piena dopo aver "assegnato il dispositivo" 512 byte, perché il dispositivo inizierà a leggere da questa finestra non appena c'è qualcosa da leggere.

Forse mi sono perso qualcosa di importante nella tua domanda, ma quello che sto dicendo è essenzialmente che devi scrivere più di 512 byte alla volta. Questo è il motivo per cui ottieni così scarso rendimento.