summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Levy <alevy@redhat.com>2011-09-21 15:51:36 +0300
committerAlon Levy <alevy@redhat.com>2011-09-21 16:08:44 +0300
commit96b19230c6780a7c877ec53bde525b5f032de0cd (patch)
tree0735e7d59214b21137f557ef0e501a2ee0dee91f
parent0ba9e87062d68bae763130d027370bd3803a72c0 (diff)
display: handle interrupt handler disappearance (rhbz 721118)
This patch handles the surprise removal whql test. The surprise removal test sends a surprise removal IRP via a filter driver to qxl.sys. The handling of that IRP is actually done in videoprt.sys, and there is no API to get notified. The side effect of the handler in videoprt.sys is disabling our interrupt handler. This patch takes care of continuing to work after our interrupt is disabled. This is in effect the opposite of what the MSDN states that a device should do after a surprise removal, since the MSDN requires every IO to fail after the surprise removal IRP has been successfully handled. On the other hand, this workaround is sufficient to pass the test. The workaround is to turn the EngWaitForSingleEvent on the io_cmd_event event into a timed wait. If it fails, we revert to synchronouse IO, which doesn't require that event at all.
-rw-r--r--display/qxldd.h39
1 files changed, 38 insertions, 1 deletions
diff --git a/display/qxldd.h b/display/qxldd.h
index c1a509f..f02a71e 100644
--- a/display/qxldd.h
+++ b/display/qxldd.h
@@ -442,6 +442,25 @@ static _inline RingItem *RingGetTail(PDev *pdev, Ring *ring)
#define WAIT_FOR_EVENT(pdev, event, timeout) EngWaitForSingleObject(event, timeout)
#endif
+/* Helpers for dealing with ENG_TIME_FIELDS */
+static _inline ULONG64 eng_time_diff_ms(ENG_TIME_FIELDS *b, ENG_TIME_FIELDS *a)
+{
+ ULONG64 ret = 0;
+
+ ret += b->usMilliseconds - a->usMilliseconds;
+ ret += 1000 * (b->usSecond - a->usSecond);
+ ret += 60000 * (b->usMinute - a->usMinute);
+ ret += 3600000L * (b->usHour - a->usHour);
+ // don't get into gregorian calendar, just ignore more then a single day difference
+ if (b->usDay != a->usDay) {
+ ret += (3600L * 24L * 1000L);
+ }
+ return ret;
+}
+
+#define INTERRUPT_NOT_PRESENT_TIMEOUT_MS 2000L
+#define INTERRUPT_NOT_PRESENT_TIMEOUT_100NS (INTERRUPT_NOT_PRESENT_TIMEOUT_MS * 10000L)
+
/* Write to an ioport. For some operations we support a new port that returns
* immediatly, and completion is signaled by an interrupt that sets io_cmd_event.
* If the pci_revision is >= QXL_REVISION_STABLE_V10, we support it, else do
@@ -449,10 +468,28 @@ static _inline RingItem *RingGetTail(PDev *pdev, Ring *ring)
*/
static _inline void async_io(PDev *pdev, asyncable_t op, UCHAR val)
{
+ ENG_TIME_FIELDS start, finish;
+ LARGE_INTEGER timeout; // 1 => 100 nanoseconds
+ ULONG64 millis;
+
if (pdev->use_async) {
EngAcquireSemaphore(pdev->io_sem);
WRITE_PORT_UCHAR(pdev->asyncable[op][ASYNC], val);
- WAIT_FOR_EVENT(pdev, pdev->io_cmd_event, NULL);
+ /* Our Interrupt may be taken from us unexpectedly, by a surprise removal.
+ * in which case this event will never be set. This happens only during WHQL
+ * tests (pnpdtest /surprise). So instead: Wait on a timer, if we fail, stop waiting, until
+ * we get reset. We use EngQueryLocalTime because there is no way to differentiate a return on
+ * timeout from a return on event set otherwise. */
+ timeout.QuadPart = -INTERRUPT_NOT_PRESENT_TIMEOUT_100NS; // negative => relative
+ DEBUG_PRINT((pdev, 15, "WAIT_FOR_EVENT %d\n", (int)op));
+ EngQueryLocalTime(&start);
+ WAIT_FOR_EVENT(pdev, pdev->io_cmd_event, &timeout);
+ EngQueryLocalTime(&finish);
+ millis = eng_time_diff_ms(&finish, &start);
+ if (millis >= INTERRUPT_NOT_PRESENT_TIMEOUT_MS) {
+ pdev->use_async = 0;
+ DEBUG_PRINT((pdev, 0, "%s: timeout reached, disabling async io!\n", __FUNCTION__));
+ }
EngReleaseSemaphore(pdev->io_sem);
DEBUG_PRINT((pdev, 3, "finished async %d\n", (int)op));
} else {