Power relationships in a bus driver

A small but important rule in WDM is that while a PDO is in D0, the parent must be in D0 as well. A very simple statement that can cause a lot of trouble ;). What I have seen is that very few WDM drivers enforce this rule (while KMDF does implement this rule, so all KMDF drivers inherit this for free) since it can be quite difficult to get right. It is hard to get right because there are a few scenarios that affect how this rule is interpreted. Here are a few scenarios that might affect its interpretation:

  • The machine is entering an Sx state and the parent is powering down
  • The machine is running (e.g. S0) and all of the children have idled out and powered down
  • The children are software devices which are not connected to any real hardware
  • A child is started while the parent is in Dx because it idled out
  • The parent who enumerated the PDO is not the power policy owner for the stack

The machine is entering an Sx state the and parent is powering down

In this scenario, if all of the power policy owners (i.e. a PPO) for your child stacks are implemented properly, there is nothing for your bus driver to do. A proper PPO implementation will pend the Sx IRP, request a Dx IRP, and once the Dx IRP is completed by the entire stack, complete the Sx irp. The OS's power manager will guarantee that the parent FDO will not see an Sx request until all of its children have completed their Sx IRPs. This means that in this case, the OS enforces the power relationship. But what happens if one of the child stacks has a PPO implementation which is wrong? For instance, it requests the Dx IRP but does not wait for it to complete and lets the Sx IRP complete asynchronously before Dx has completed. It introduces a race condition. The Dx could complete before the parent is powered off and everything is OK, or it could lead to the parent powering off before the child. What happens if the PDO does not have a PPO at all? In this case, the PDO is always in D0. In both cases, you can have a parent that is in Dx and a child that is in D0. What should you do as a bus driver in this case? You can't block the Sx IRP until all of the children have powered down because that could prevent the machine from actually reaching the intended Sx state. So you do nothing because this is a bug in the PDO's PPO and there is nothing you can do about.

The machine is running and all of the children have idled out and powered down

This is the trickiest scenario to implement. You do not have Sx irps to guard the states, so the driver must guard its own state. When you are in this state, the bus may want to power down as well. To implement a power down of the parent, two things must occur. First you must decide to power down (typically by starting a timer so that you don't immediately power down the parent). Second, you must put in a barrier so that if a child PDO stack decides to power up (as indicated to the bus driver by a D0 IRP arriving at the PDO) after you have made the decision to power down or are in the middle of powering down, the PDO's D0 IRP is pended until the parent is completely powered down and then powered back up again. Once the parent returns to D0, the PDO's D0 IRP can be processed. Getting all of this right is not trivial, KMDF does it, but even in KMDF it took us until v1.5 to handle these small edge cases that are hard to hit.

The children are software devices

Since the children are software devices (a software device is one that does not control hardware nor is it enumerated by a bus that controls hardware (like USB)), device power state has no real effect on the functionality of the child stack. This means that the PDO's PPO will never put the device into Dx. The bus driver doesn't care about the parent/child power relationship in this case either because there is nothing gained by it. You can only take this shortcut in a WDM driver. In KMDF we always enforce the rule.

A child is started while the parent is in Dx because it is idled out

Let's say that the parent is in a Dx state and then a child stack is started. (You can get into this state if the PDO was previously enumerated and disabled and then the user enables the PDO stack.) This is a variation on the previous idle scenario. In this scenario, the relationship must be enforced, so the PDO's handling of the PnP start IRP must be asynchronously pended until the parent moves into D0. After the parent returns to D0, the child's PnP start IRP processing can resume. In KMDF, the PDO calls the equivalent of WdfDeviceStopIdle() on the parent WDFDEVICE when processing the start IRP (and calls WdfDeviceResumeIdle() on the parent when it is put into the powered down state so that the parent can optionally idle out). The power state machine in KMDF makes handling this scenario very simple.

The parent who enumerated the PDO is not the power policy owner for the stack

Let's take the previous scenario, but remove the fact that the enumerator is the PPO for the parent stack. Now, there is no way for the PDO's enumerator to guarantee that the parent is in D0 when the child PDO is in D0. This completely breaks the rule, but there is nothing you can do as a bus driver (KMDF is aware of this scenario and doesn't try to change the parent enumerator's power state). What this boils down to is that the child stack is a software device (see previous scenario for a description of a software device) where the device power state does not matter.

Comments