Таки никто usb-драйверы не пишет?

Пока решил эту проблему небольшим "костылём". На всякий случай оставлю здесь — может кому пригодится.
В CompletionRoutine при IRQL == DISPATCH_LEVEL я отменяю свою очередь через IoCancelIrp, а не с помощью URB_FUNCTION_ABORT_PIPE. Вызвать эту функцию на IRQL == DISPATCH_LEVEL у меня так и получилось.

После того, как в очереди не остаётся висящих запросов к bulk-пайпу, выставляю флажок ResetPipeFlag в контексте трансфера и жду, когда юзер-мод соизволит прочитать данные из моего устройства. Это будет происходить через вызов DispatchRead на IRQL == PASSIVE_LEVEL. По имеющемуся флагу вызываю URB_FUNCTION_RESET_PIPE с помощью IoBuildDeviceIoControlRequest и потом возобновляю очередь запросов.
Естественно, не забываю делать IoReuseIrp для созданных вручную IRP перед повторным вызовом IoCallDriver — в этом случае это критично, потому что флаг Cancel надо очистить.
В итоге что и требовалось — юзер-мод не догадывается о проблемах. Можно, конечно, оповещать пользователя о сбоях, но это уже другая песня.
Если есть более нормальные методы, поделитесь, не жадничайте!