2011-11-01 9 views
5

Estoy escribiendo un controlador kernel de Linux para un dispositivo USB personalizado que usará puntos finales masivos, todo parece funcionar bien, sin embargo, estoy obteniendo velocidades de datos muy lentas Específicamente, toma ~ 25 segundos escribir y leer datos de 10MB. Intenté esto con un sistema integrado y una VM Linux ejecutándose en una PC razonable con resultados similares.rendimiento muy pobre (~ 0.4MB/s) con Linux usb controlador de kernel de transferencia masiva y hardware loopback

Estoy utilizando un kit de desarrollo EZ-USB FX2 de Cypress como placa objetivo. Está ejecutando el firmware bulkloop que establece dos puntos finales de entrada y dos de salida. Cada punto final tiene doble buffer y admite ventanas de 512 bytes. El firmware sondea los puntos finales a través de un ciclo while (1) en main(), sin reposo, y copia los datos de out en los puntos finales cuando esos datos están disponibles usando autoposicionamientos. Me han dicho que esto puede mover los datos de manera justa en Windows utilizando su aplicación específica, pero no ha tenido la oportunidad de verificar esto.

Mi código (porciones relevantes a continuación) llama a una función llamada bulk_io en la rutina de sonda del dispositivo. Esta función crea un número (URB_SETS) de out urbs que intentan escribir 512 bytes en el dispositivo. Cambiar este número entre 1 y 32 no cambia el rendimiento. Todos están copiando desde el mismo buffer. El controlador de devolución de llamada para cada operación de escritura a un punto final externo se utiliza para crear una urb de lectura en el punto final correspondiente. La devolución de llamada de lectura crea otra urb de escritura hasta que haya alcanzado el número total de solicitudes de escritura/lectura que quiero ejecutar a la vez (20,000). Estoy trabajando ahora para insertar la mayoría de las operaciones en las funciones de devolución de llamada en las mitades inferiores en caso de que estén bloqueando otras interrupciones. También estoy pensando en reescribir el firmware bulk-loop para que Cypress FX2 use interrupciones en lugar de sondeo. ¿Hay algo aquí que parece fuera de lo normal para hacer que el rendimiento sea tan bajo? Gracias de antemano. Por favor, avíseme si desea ver más código, este es solo un controlador básico para probar E/S al Cypress FX2.

Ésta es la salida de punto final la función de devolución de llamada de escritura:

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

Esta es la de punto final función de devolución de leer:

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

Esta función se llama a la creación de la urbs iniciales:

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

Editar 1 I verifi ed que udev-> == velocidad 3, por lo USB_SPEED_HIGH, significando esto no se debe a Linux piensa que esto es un dispositivo lento ....

Editar 2 me movía todo en las devoluciones de llamada relacionados con la creación urb (kmalloc, enviar) y la liberación en las mitades inferiores, el mismo rendimiento.

+3

Así que el misterio ya no existe. Modifiqué el firmware CY7C68013A 'bulkloop' para alternar un GPIO cuando está moviendo data/arming endpoints y estaba gastando ~ 80% de sus ciclos en esa función. Parece que el 8051 core touch de los búferes USB reduce el rendimiento a ~ 0.5MB/s como se muestra arriba. Seguí adelante y comparé con su demo de CylusB lib Windows bulkloop y obtuve un desempeño mucho peor, alrededor de 0.1MB/s. En conclusión, usar el firmware bulkloop no es una buena prueba del rendimiento del controlador USB. Lo intentaré con un FPGA alimentando los datos del CY7C68013A a continuación. – armguy

+0

solo un pequeño punto aquí, debe mantener las devoluciones de llamada URB lo más pequeñas posible (por ejemplo, simplemente establecer una bandera) –

Respuesta

1

En mi experiencia, leer y escribir en pequeños trozos no es muy efectivo.

Estoy utilizando un kit de desarrollo FX2 EZ-USB de Cypress como el tablero de destino. Ejecuta el firmware de bulkloop que configura puntos finales dos y dos puntos finales. Cada punto final tiene doble buffer y admite ventanas de 512 bytes.

Esto no significa que no pueda escribir más de 512 bytes a la vez.

Intentaría escribir al menos 4096 bytes a la vez, porque ese es el tamaño de página estándar (quizás no tan estándar en los sistemas integrados).Si eso funcionara, trataría de escribir tanto como 1/4 de megabyte a la vez, y aún más si eso funcionó.

El punto clave aquí es saber cuándo la ventana de escritura del dispositivo está llena. Cuando lo es, llamará a todas las devoluciones de llamada u obtendrá esa información por cualquier otro medio y la usará para indicarle a su aplicación que deje de escribir.

Tenga en cuenta que la ventana no estará llena después de "dar al dispositivo" 512 bytes, porque el dispositivo comenzará a leer desde esta ventana tan pronto como haya algo que leer.

Quizás me he perdido algo importante en su pregunta, pero lo que estoy diciendo es, en esencia, que tiene que escribir más de 512 bytes a la vez. Es por eso que obtienes un rendimiento tan pobre.

Cuestiones relacionadas