Currently the driver uses memory structure on the stack
as DMA buffer. To be compatible with IOMMU, we change it
to use common DMA buffer. On D0 entry the driver allocates
single 4K block and uses slices from it for control queue
commands. The entry is 8 bytes, so the buffer is enough
for 512 entries. The driver usually uses just one entry
as control commands do not happen often.
Signed-off-by: Yuri Benditovich <[email protected]>
Zero interval makes KeDelayExecutionThread to call NtYieldExecution with
minimal additional actions, but it is not documented. It is changed to minimal relative
interval value (-1).
Using KeDelayExecutionThread with zero timeout interval cause current thread
to relinquish the remainder of its time slice to other thread in most cases.
This allows to make small delay on PASSIVE_LEVEL and optimize CPU usage.
It looks slightly more efficient then using KeDelayExecutionThread which is
actually a busy loop.
Signed-off-by: Ilya Rudakov <[email protected]>
This patch is a fix for very annoying problem in the production. It was
very hard to understand what is going on and why. From the user point
of view the situation looks like
dev: virtserialport, id "channel0"
chardev = "charchannel0"
nr = 1 (0x1)
name = "org.qemu.guest_agent.0"
port 1, guest off, host off, throttle off
and the device comes into this state under unknown circumstances.
We've found out that this problem is caused by a breakdown in the
control channel. The host has stopped responding to the control messages
at some moment. Sometimes channel could be fixed after qemu-qa service
restart, sometimes not. From the technical point of view there is the
following problem:
The amount of virtqueue_get_buf calls from VIOSerialSendCtrlMsg is
limited to RETRY_THRESHOLD times(400). Usually this is more than enough.
The amount of retries in the common case in 1-2. But unfortunately
sometimes host needs MUCH more time to handle message. Thus there is
a probability that the polling counter will exceeds RETRY_THRESHOLD.
In our production and load testing this probability fires 1-2 times
a week. Thus here loop ends, but the queue still contains the element
with the address on VIOSerialSendCtrlMsg stack. This buffer will become
invalid after the return from the function. Obviously QEMU will see the
garbage once it will get a chance to process such orphaned message.
It ignores it without guest notification and thus the vioserial is
stuck in some intermediate state.
We can't just remove RETRY_THRESHOLD limit. In this case there is a
risk of getting DPC_WATCHDOG bugcheck due to long spin wait on
DPC_LEVEL.
This patch splits in/out queues lock CVqLock on two, with WaitLock
COutVqLock being used for c_ovq. In this case virtqueue_get_buf poll
loop is executed on PASSIVE_LEVEL. The restriction to the amount of
retries is removed accordingly. WorkItem is used to low the current
IRQL where necessary.
Spinlock is better solution to synchronize such a small and fast code
and was rightfully used to lock c_ivq and c_ovq access. Thus it is kept
for CInVqLock to lock c_ivq. Using WaitLock for c_ovq leads to
insignificant decrease of the control message sending rate due to
walking through dispatcher every time on lock acquire/free and a possible
thread preemtion while running locked code, but reliability is more
important than control channel speed.
Signed-off-by: Ilya Rudakov <[email protected]>
Signed-off-by: Denis V. Lunev <[email protected]>
Related to commit 8fe2811b. If msi was not enabled (e.g. Windows
XP), an invalid interrupt object was used when sending notification
that the host is connected.
The previous write function didn't handle well the case when the
virtqueue was full and caused the guest to stop responding. New
implementation fixed that and add a support for a non-blocking
write (returns immediately if write will block).