Как работает IoCallDriver?

Тема в разделе "WASM.WIN32", создана пользователем compnet, 20 июл 2005.

  1. compnet

    compnet New Member

    Публикаций:
    0
    Регистрация:
    19 сен 2004
    Сообщения:
    10
    Адрес:
    Russia
    При написании USB драйвера возникла проблема разбиения оного большого пакета на мелкие - по азмеру буфера девайса. Мелкомягкие в своем ddk\src\wdm\usb\bulkusb делают это следуюшим образом: послыют пакет максимального размера и ставят процедуру завершения. Внутри которой проверяю - а не последний ли это пакет. Если не последний, то из CompletionRoutine выполняют IoCallDriver к нижележфщему драйверу со следующими данными для девайса. При максимально разрешенном размере пакета таких итераций может быть 200 (у MS).

    Реализовал подобное, вознкла следующая проблема - при более 100 операций винда отправляется в ребут даже не посинев. Логи показали, что процедуры получают управление в следующем порядке

    start WriteDispatch

    before IoCallDriver in WriteDispatch

    IoCallDriver

    in IoCompletion 1

    in IoCompletion 2

    ...

    end of IoCompletion2

    end of IoCompletion1

    after IoCallDriver in WriteDispatch

    end of Write Dispatch



    Получается, что WriteDispatch возвращает STATUS_PENDING _после_ того, как IRP был завершен в процедуре iocompletion. Можно конечно реализовать по другому, но хотелось бы услышать мнение опытных людей
     
  2. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Из статьи Four-F Жизненный цикл IRP

    Правило: Завершать IRP с кодом STATUS_PENDING нельзя.



    ну а сама IoCallDriver работает примерно так:
    Код (Text):
    1. /**
    2.  *  @return status from driver's dispatch routine.
    3.  */
    4. NTSTATUS
    5.   call_driver(
    6.     DEVICE_OBJECT     * device, ///< Pointer to device object to which the IRP should be passed.
    7.     IRP               * irp     ///< Pointer to IRP for request.
    8.     )
    9. {
    10.     irp->CurrentLocation--;
    11.  
    12.     if( irp->CurrentLocation <= 0 )
    13.         KeBugCheckEx(NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) irp, 0, 0, 0);
    14.  
    15.     IO_STACK_LOCATION * stack = IoGetNextIrpStackLocation(irp);
    16.     irp->Tail.Overlay.CurrentStackLocation = stack;
    17.     stack->DeviceObject = device;
    18.  
    19.     return device->DriverObject->MajorFunction[stack->MajorFunction](device, irp);
    20. }
    21.  
     
  3. compnet

    compnet New Member

    Публикаций:
    0
    Регистрация:
    19 сен 2004
    Сообщения:
    10
    Адрес:
    Russia
    Упс, забыл указать, что я создаю второй irp packet, который и отправляется гулять вниз по стеку, а iomarkirppending и статус status_pending возвращается для исходного -> так что завершается он _один_ раз, так как не попадает к нижележащим драйверам.

    Собственно, проблема состоит в том, что при вызове iocalldriver процедура обработки ниднего драйвера выполняется _до_ возвращения управления.

    Я не знаю, может это правильно, но как тогда должен работать пример мелкомягких?
     
  4. Four-F

    Four-F New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2002
    Сообщения:
    1.237
    Если нижестоящий драйвер синхронно завершает IRP, то теоретически порядок вызова процедур будет именно таким, как ты привел. Смотри: в WriteDispatch ты вызываешь IoCallDriver, нижестоящий драйвер заполняет буфер и завершает IRP и мы окажемся в IoCompletion 1. Она опять шлет IRP (не возвращая управления), нижестоящий драйвер заполняет буфер и опять завершает IRP, и мы окажемся в IoCompletion 2. И так до IoCompletion X, которая наконец вернет управление и всё отмотается назад и мы выйдем из самого первого IoCallDriver, вызванного в WriteDispatch.



    Если же нижестоящий драйвер ставит IRP в очередь, то порядок будет другим. Как должен работать пример мелкомягких я не знаю. Но похоже в твоём случае происходит банальное переполнение стека, ведь IoCompletion фактически косвенно вызывает сама себя рекурсивно. Ядреный стек маленький (~12Кб), так что очень похоже на то. Воткни IoGetRemainingStackSize перед вызовом IoCallDriver в процедуре завершения и рез-т выводи в лог. Возможно это прояснит ситуацию.
     
  5. TarasCo

    TarasCo New Member

    Публикаций:
    0
    Регистрация:
    2 фев 2005
    Сообщения:
    106
    2 compnet:

    А Вы нас в заблуждение не вводите? Специально посмотрел пример bulkusb ( в свое время я на основе его написал свой работающий драйвер ). Там не так, как вы описываете. Есть процедура пакетного чтения/записи. Она вызвается, если клиент пожелал иницировать операцию ввода/вывода, больше, чем устройство может возратить за один раз. Дабы не заниматься буферизацией и при этом прочесть максимально возможно кол-во данных. Реализовано так: в процедуре в цикле содается N IRP, которые засылаются вниз (в этом же цикле). После чего поток переводится в ожидание события окончания всей транзакции. В процедур завершения никаких IoCallDriver нет - соответсвенно нет переполнения стека. При обработке полседнего IRP выставялется событие, разблокирующее поток. Так по крайней мере в DDK 2000
     
  6. compnet

    compnet New Member

    Публикаций:
    0
    Регистрация:
    19 сен 2004
    Сообщения:
    10
    Адрес:
    Russia
    Фууу, разобрался - надо было просто выспаться.

    Большое спасибо всем, принявшим участие в обсуждении.



    To Four-F:

    Ты совершенно прав, именно это и проиcходило...



    To TarasCo:

    Честно говоря, не знаю, как реализовано в DDK2k, у меня xp, но там процедура read/write dispatch реализована именно таким образом. Если интересно, такой же кусок есть в драйвех cypress semiconductor, описан в книге Уолтера Они и т.д.



    Если кому инетересно, могу сказать в чем было дело:

    при создании irp'а вместо irp_mj_internal_device_control написал irp_mj_device_control и обнаружил это только вчера. Схема, предложенная МС, работает, если дайвер делает отложенную обработку (респект то Four-F), если же он все выполняет в dispatch_control и возващает success, то происходит банальное переполнение стека.