очень низкая производительность (~ 0,4 МБ / с) с драйвером ядра Linux для массовой передачи USB и аппаратной обратной связью - PullRequest
5 голосов
/ 01 ноября 2011

Я пишу драйвер ядра Linux для пользовательского USB-устройства, которое будет использовать массовые конечные точки, все, кажется, работает нормально, однако я получаю очень медленные скорости передачи данных.В частности, для записи и чтения данных объемом 10 МБ требуется ~ 25 секунд.Я попробовал это на встроенной системе и на виртуальной машине Linux, работающей на приемлемом ПК с похожими результатами.

Я использую комплект разработки EZ-USB FX2 от Cypress в качестве целевой платы.Он работает с встроенным программным обеспечением, которое устанавливает две входные и две конечные точки.Каждая конечная точка имеет двойную буферизацию и поддерживает 512-байтовые окна.Микропрограмма опрашивает конечные точки с помощью цикла while (1) в main (), без спящего режима, и копирует данные из оконечных точек в конечные точки, когда эти данные доступны с помощью автопойнтеров.Мне сказали, что это может справедливо перемещать данные в Windows с помощью их конкретного приложения, но у меня не было возможности проверить это.

Мой код (соответствующие части ниже) вызывает функцию под названием bulk_io в процедуре проверки устройства.Эта функция создает число (URB_SETS) выходных urb-адресов, которые пытаются записать 512 байт на устройство.Изменение этого числа от 1 до 32 не влияет на производительность.Все они копируют из одного и того же буфера.Обработчик обратного вызова для каждой операции записи в конечную точку out используется для создания urb для чтения в соответствующей конечной точке.Обратный вызов чтения создает другой urb-адрес записи, пока я не достигу общего числа запросов на запись / чтение, которые я хочу выполнить за один раз (20 000).Сейчас я работаю над тем, чтобы перенести большинство операций в функциях обратного вызова в нижние половины, если они блокируют другие прерывания.Я также думаю о переписывании встроенного программного обеспечения для Cypress FX2, чтобы использовать прерывания вместо опроса.Есть ли здесь что-нибудь необычное, чтобы сделать производительность настолько низкой?Заранее спасибо.Пожалуйста, дайте мне знать, если вы хотите увидеть больше кода, это всего лишь простой драйвер для тестирования ввода-вывода Cypress FX2.

Это функция обратного вызова записи конечной точки:

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

Это функция обратного вызова чтения в конечной точке:

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

Эта функция вызывается для установки начальных URL:

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

Редактировать 1 Я проверил, что udev-> speed == 3, поэтому USB_SPEED_HIGH, то есть это не потому, что Linux думает, что это медленное устройство ....

Редактировать 2 Я переместил все вобратные вызовы, связанные с созданием urb (kmalloc, submit) и освобождением в нижние половины, та же производительность.

1 Ответ

1 голос
/ 10 октября 2012

Чтение и запись маленькими кусочками по моему опыту не очень эффективны.

Я использую комплект разработчика EZ-USB FX2 от Cypress в качестве целевой платы. Он работает с встроенным программным обеспечением, которое устанавливает две входные и две конечные точки. Каждая конечная точка имеет двойную буферизацию и поддерживает 512-байтовые окна.

Это не означает, что вы можете одновременно записывать в него не более 512 байт.

Я бы попытался записать на него не менее 4096 байт за раз, потому что это стандартный размер страниц (возможно, не столь стандартный во встроенных системах). Если бы это сработало, я бы попытался записать до 1/4 мегабайта за раз, а затем даже больше, если бы это сработало.

Ключевым моментом здесь является знание того, когда окно записи устройства заполнено. Когда это так - он будет вызывать любые обратные вызовы, или вы получите эту информацию любым другим способом и будете использовать ее в качестве сигнала вашего приложения о прекращении записи.

Обратите внимание, что окно не будет заполнено после того, как вы "дадите устройству" 512 байт, потому что устройство начнет чтение из этого окна, как только будет что-то прочитать.

Возможно, я упустил что-то важное в вашем вопросе, но я говорю, что по сути вы должны писать более 512 байт за раз. Вот почему вы получаете такую ​​низкую производительность.

...