Suppose the following scenario:
- Thread A locks CHN, sets the trigger to ABORT, and unlocks CHN. It then locks PCM and _removes_ CHN from the list.
- In the meantime, since thread A unlocked CHN, thread B has locked it, set the trigger to START, unlocked it, and is now blocking on PCM held by thread A.
- At the same time, thread C locks CHN, sets the trigger back to ABORT, unlocks CHN, and is also blocking on PCM. However, when thread A unlocks PCM, because thread C is higher-priority than thread B, it picks up the PCM lock instead of thread B, and because CHN is already removed from the list, and thread B hasn't added it back yet, we take a page fault in CHN_REMOVE() by trying to remove a non-existent element.
To fix this, have PCM locked during both the trigger setting, as well as
adding/removing a channel to/from the list, so that we have a single
section instead of two.
The same scenario applies to vchan_trigger(), where the parent channel's
lock acts as the PCM one.
Sponsored by: The FreeBSD Foundation
MFC after: 2 days