Удаление WDM-устройства в драйвере PnP

У меня вопрос об удалении устройства.

Когда мы хотим уведомить менеджера PnP об исчезновении устройства, мы вызываем IoInvalidateDeviceRelations с помощью BusRelations. После этого ОС отправит IRP_MN_QUERY_DEVICE_RELATIONS запрос с BusRelations. В этом обработчике запроса мы исключим устройство из массива и выполним другую необходимую работу, чтобы «отключить» его от шины, а также установим флаг RemovePending в его расширении устройства.

Я не понимаю, как поступать с входящими запросами ввода-вывода на устройство после того, как оно становится «удалить ожидающим» и до того, как ОС отправит IRP_MN_REMOVE_DEVICE запрос. Должны ли мы проверить флаг RemovePending и вернуть STATUS_DEVICE_DOES_NOT_EXIST или действовать как обычно?

Теперь представьте, что запрос IRP_MN_REMOVE_DEVICE наконец-то пришел. MSDN говорит, что мы должны вызвать IoReleaseRemoveLockAndWait, который освобождает текущее получение блокировки удаления, предотвращает последующие приобретения и ждет, пока освобождаются существующие приобретения. Таким образом, это заставляет нас всегда запрашивать удаление блокировки внутри обработчика запроса PnP с помощью IoAcquireRemoveLock; и отпустите его с помощью IoReleaseRemoveLockAndWait для IRP_MN_REMOVE_DEVICE или с помощью IoReleaseRemoveLock для других второстепенных кодов.

Я не понимаю, зачем нам снимать блокировку внутри обработчика запросов PnP? Насколько я понимаю, нам нужно снимать блокировку только для ожидающих irp и освобождать ее, когда такой irp завершен. Так что ребята из Windows могли предоставить нам подпрограмму IoWaitForExistingRemoveLocks вместо IoReleaseRemoveLockAndWait.

Извините, если сумбурно, просто не могу разобраться. Спасибо.


person new_user    schedule 01.08.2017    source источник


Ответы (1)


После этого ОС отправит IRP_MN_QUERY_DEVICE_RELATIONS запрос с BusRelations. В этом обработчике запроса мы исключим устройство из массива и выполним другую необходимую работу, чтобы «отключить» его от шины, а также установим флаг RemovePending в его расширении устройства.

здесь нужно только исключить устройство из массива и установить флаг RemovePending в его расширении устройства. но выполните другую необходимую работу, чтобы "отключить" его от шины - нужно делать только тогда, когда вы обрабатываете IRP_MN_REMOVE_DEVICE (после того, как устройство не было включено в последний ответ драйвера шины на запрос IRP_MN_QUERY_DEVICE_RELATIONS для BusRelations - или другими словами - когда флаг RemovePending в его расширение устройства)

как обрабатывать входящие запросы ввода-вывода к устройству после того, как оно становится «удалить ожидающим» и до того, как ОС отправит IRP_MN_REMOVE_DEVICE запрос. Должны ли мы проверить флаг RemovePending и вернуть STATUS_DEVICE_DOES_NOT_EXIST или действовать как обычно?

я думаю, что возможно оба поведения - вы можете обработать его как обычно и также можете вернуть STATUS_DEVICE_DOES_NOT_EXIST. и предположим следующую ситуацию - вы получаете запрос ввода-вывода одновременно с процессом удаления устройства. когда вы проверяете флаг RemovePending - он еще не установлен. и вы начинаете обрабатывать запрос "как обычно". но сразу после того, как вы проверите флаг RemovePending внутри запроса ввода-вывода, вы можете установить его при обработке запроса IRP_MN_QUERY_DEVICE_RELATIONS с помощью BusRelations. и эта ситуация напрямую связана со смыслом использования Remove Locks или Защита от перегрузки.

мы действительно можем использовать Run-Down Protection вместо Remove Locks почти таким же образом и точно в тех же местах и ​​по той же причине, что и Remove Locks. и я думаю, что API-интерфейс защиты от износа (более новый сравните удаление блокировок) - лучший дизайн и лучше для использования (однако разница минимальна)

Я не понимаю, зачем нам снимать блокировку внутри обработчика запросов PnP?

прежде всего обратите внимание, что о снятии блокировки сказано только в Удаление устройства в функциональном драйвере. у вас, как я понимаю, есть не функция, а драйвер шины, поэтому Удаление устройства в драйвере шины больше подходит для вас. и в документации для Удаление устройства в функциональном драйвере существует серьезная ошибка — рекомендуется сначала позвонить IoReleaseRemoveLockAndWait в точке 4 - до точки 8< /strong> — передать запрос IRP_MN_REMOVE_DEVICE следующему драйверу. но правильно должно быть

Драйвер должен вызвать IoReleaseRemoveLockAndWait после передачи запроса IRP_MN_REMOVE_DEVICE следующему нижестоящему драйверу и до того, как он освободит память, вызовет IoDetachDevice или IoDeleteDevice.

это правильно и указано в Использование снятия блокировок и IoReleaseRemoveLockAndWait. забавно, что в старых версиях msdn было

вызовите IoReleaseRemoveLockAndWait прежде чем он пройдет..

но теперь это исправлено. почему после? потому что следующий более низкий драйвер может ожидать некоторых IRP (на которых мы вызываем IoAcquireRemoveLock или ExAcquireRundownProtection) и завершать их только тогда, когда получен IRP_MN_REMOVE_DEVICE, а наш драйвер вызывает IoReleaseRemoveLock или ExReleaseRundownProtection только тогда, когда этот IRP будет завершен. в результате, если вызов IoReleaseRemoveLockAndWait или ExWaitForRundownProtectionRelease до передает удаление IRP следующему нижестоящему драйверу - мы можем ждать здесь вечно - следующий нижестоящий драйвер не может завершить какой-то IRP (пока не получит запрос на удаление) и мы не освобождать снять блокировку или защиту от износа.

так для чего нам нужно снять блокировку или защиту от износа? потому что мы можем получить IRP_MN_REMOVE_DEVICE одновременно с другими запросами ввода-вывода. и эти запросы ввода-вывода могут использовать некоторые ресурсы на устройстве. другого размера, когда мы обрабатываем IRP_MN_REMOVE_DEVICE, мы уничтожаем эти ресурсы. что будет, если мы будем использовать какой-то ресурс в запросе ввода-вывода после того, как он будет уничтожен в IRP_MN_REMOVE_DEVICE? думаю не нужен ответ. для предотвращения этого и существуют блокировки удаления или защита от износа. перед использованием любого ресурса (который будет уничтожен при удалении) необходимо вызвать IoAcquireRemoveLock или ExAcquireRundownProtection и использовать его, только если возвращается статус ok. после того, как мы закончим использовать вызов ресурса IoReleaseRemoveLock или ExReleaseRundownProtection. а в IRP_MN_REMOVE_DEVICE мы называем IoReleaseRemoveLockAndWait или ExWaitForRundownProtectionRelease. после того, как этот вызов возвращается - мы можем быть уверены, что никто не использует наши ресурсы и никогда не будет использоваться больше (вызовы IoAcquireRemoveLock или ExAcquireRundownProtection возвращают статус ошибки (false)). в этот момент мы можем безопасно начать уничтожать ресурсы: освобождать память (и т. д.), вызывать IoDetachDevice, IoDeleteDevice.

указатель на следующее ниже устройство - это тоже ресурс, который мы используем в процессе IO request и уничтожаем в IRP_MN_REMOVE_DEVICE (вызовом IoDetachDevice). действительно ли правильный вызов IofCallDriver(_nextDeviceObject, Irp); (во время обработки некоторого запроса ввода-вывода) после вызова IoDetachDevice(_nextDeviceObject); (внутри IRP_MN_REMOVE_DEVICE)? потому что это удаление блокировок (или я использую здесь защиту от рандауна) всегда используется в функциях и драйверах фильтров. для драйвера шины, где у нас обычно нет указателя на следующее более низкое устройство (PDO не подключен к другому устройству, когда FDO подключен к PDO и фильтр всегда к чему-то прикреплен) - может быть и не нужны замки (или защита от износа вообще). это зависит - существуют ли другие ресурсы - используются и уничтожаются (при удалении).

а для устройств шины - обычная ситуация, когда мы получили IRP_MN_REMOVE_DEVICE 2 раза - сначала перед устройством, помеченным как RemovePending - поэтому переходим к пункту 4. и после устройства, помеченного как RemovePending (поэтому устройство не было включено в последний ответ драйвера шины на IRP_MN_QUERY_DEVICE_RELATIONS запрос на BusRelations) окончательно уничтожаем ресурсы и вызываем IoDeleteDevice

person RbMm    schedule 01.08.2017
comment
@RbMmСпасибо за ответ, это очень ясно. Таким образом, идея состоит в том, чтобы защитить ресурсы (объект устройства, его расширение и т. д.) от освобождения, пока мы обрабатываем другой запрос. То, что я до сих пор не понимаю: мы храним снятие блокировки в расширении устройства, поэтому для его получения нам нужно получить доступ к этому расширению. Где гарантия, что к моменту обращения к нему вызов IoDeleteDevice еще не был выполнен (поэтому объект устройства и его расширение освобождаются)? - person new_user; 03.08.2017
comment
@new_user - если кто-то отправляет нам запрос ввода-вывода - он должен иметь указатель на DeviceObject и должен быть уверен, что у него есть ссылка на него (DeviceObject). поэтому DeviceObject и DeviceExtension действительны (как хранилище памяти). а вот состояние DeviceObject - уже другой вопрос - есть ссылка на объект - не гарантируется его состояние. только достоверность памяти. и защита от износа, используемая для защиты части объекта, которая может быть уничтожена до того, как будет уничтожен сам объект. - person RbMm; 03.08.2017
comment
@new_user - например, я выделяю буфер памяти devext->Buf на стартовом устройстве. и io запросы использовали этот Buf. у кого-то есть указатель на мое устройство (или даже файл, открытый на нем) и отправить на него ioctl. сам объект устройства будет действительным (как хранилище памяти) до тех пор, пока отправитель не выпустит собственную ссылку (закроет файл). даже если мы вызовем IoDeleteDevice (память будет освобождена после перехода последней ссылки в 0). но если мы получили запрос на удаление одновременно с этим ioctl и не будем использовать защиту от переполнения - мы можем освободить Buf до тех пор, пока ioctl не использует его. - person RbMm; 03.08.2017
comment
вызов ExWaitForRundownProtectionRelease дать следующий - мы ждем, если кто-то использует Buf в это время. и сразу отключите больше использования Buf. после этого мы сможем безопасно освободиться Buf - person RbMm; 03.08.2017
comment
@RbMmGreat, спасибо. Извините, если я слишком любопытен, у меня все еще есть один вопрос. У меня есть обработчики DispatchDeviceControl, DispatchCleanup, DispatchClose. Эти запросы отправляются на шину FDO из пользовательского режима, другими словами, у нас есть файл, открытый на шине FDO (таким образом, гарантируется действительность шины FDO и ее расширения в качестве хранилища памяти), но нам нужно получить доступ к дочернему PDO для выполнения запроса (мы использовать понятие пространства имен устройств): ChildPDO = (DEVICE_OBJECT*)((IoGetCurrentIrpStackLocation(Irp))->FileObject->FsContext); - person new_user; 04.08.2017
comment
@RbMmI опасаюсь, что мы получим запрос IRP_MN_REMOVE_DEVICE при обработке одного из вышеупомянутых запросов. Счетчик ссылок для дочернего PDO упадет до нуля, и память будет освобождена. Я чувствую, что нам нужно создать дополнительную ссылку для дочернего PDO и выпустить его в обработчике запросов DispatchClose, но я не уверен. - person new_user; 04.08.2017
comment
Я думаю, что понял. ОС будет увеличивать счетчик ссылок на заданный файловый объект перед вызовом DispatchDeviceControl или DispatchCleaunup; ОС будет уменьшать счетчик ссылок на данный файловый объект, когда мы завершим запрос. Это означает, что DispatchDeviceControl и DispatchCleaunup могут работать одновременно, а DispatchClose — нет, мы получим его только после того, как выполним все остальные запросы. Следовательно, мы можем безопасно выпустить ссылку на дочерний PDO в DispatchClose, в то время как менеджер PnP будет иметь свою собственную ссылку. - person new_user; 04.08.2017
comment
в общем, вы FDO поддерживаете список дочерних элементов PDO и удаляете PDO из списка или когда пользователь закрывает связанный файловый объект (вы отмечаете этот pdo и вызываете IoInvalidateDeviceRelations) или пользователь выбирает устройство извлечения. в этом случае сначала ваш pdo получил IRP_MN_REMOVE_DEVICE (здесь вы его не уничтожаете, потому что он пронумерован, а только отмечаете. потом fdo получает BusRelations - вы удаляете этот pdo из списка и даете ссылку на него бесплатно. а потом вы fdo второй раз получаете IRP_MN_REMOVE_DEVICE - вот вам уже удалите его, так как он не пронумерован у меня точно такой же софт. - person RbMm; 04.08.2017
comment
@new_user вам нужно защитить доступ к pdo (через FileObject->FsContext), скажем, с помощью AcquirePushLockShared/ReleasePushLock на FDO. также AcquirePushLockShared/ReleasePushLock используется при обработке BusRelations. - person RbMm; 04.08.2017
comment
поэтому на ioctl, когда вам нужен доступ PDO (как указатель FileObject->FsContext), вы вводите какой-то критический сек - посмотрите, все еще ли это не 0, и если да - addref (если нужно, отправьте ему io) или пометьте как ожидание удаления (при закрытии или очистке) и выход крит сек. затем отправьте запрос и отпустите. или позвоните InvalidateDeviceRelations. в дескрипторе BusRelations - вы также вводите тот же critsec для синхронизации доступа к списку pdo с пользовательским режимом ioctl. это отдельно от использования защиты от истощения на PDO. так что одна защита на PDO (если нужно) другая на FDO. это самые тонкие точки. тяжело описать все на чистом английском здесь. :) - person RbMm; 04.08.2017
comment
@RbMmBy выберите устройство для извлечения, вы имеете в виду отправить IOCTL_STORAGE_EJECT_MEDIA? Если да, то правильно ли я понимаю, что нам не нужно удалять PDO для таких устройств, как дисковод (поскольку он поддерживает понятие извлечения носителя), и нам нужно удалять PDO для таких устройств, как USB-накопитель (поскольку он сам является носителем)? Извините, если вопрос глупый. - person new_user; 07.08.2017
comment
@new_user - я имею в виду, что пользователь нажимает safely remove hardware - в этом случае вы не получаете никакого ioctl, но сначала ваш pdo получил IRP_MN_REMOVE_DEVICE - person RbMm; 07.08.2017
comment
@RbMmСпасибо. А что делать, если объект bus device получает IRP_MN_SURPRISE_REMOVAL, а потомки все еще подключены? Будут ли они автоматически получать IRP_MN_REMOVE_DEVICE или нам следует пометить их всех и вызвать IoInvalidateDeviceRelations? - person new_user; 12.08.2017
comment
Спасибо, я пропустил это: после того, как этот IRP завершится успешно и все открытые дескрипторы устройства будут закрыты, диспетчер PnP отправляет запрос IRP_MN_REMOVE_DEVICE в стек устройства. Таким образом, к тому времени, когда мы получим IRP_MN_REMOVE_DEVICE, не будет прикрепленных дочерних элементов, поскольку мы инициируем удаление дочерних элементов, когда закрываем дескриптор или когда мы safely remove hardware. - person new_user; 12.08.2017