via-cuda: Avoid TREQ race condition
authorFinn Thain <fthain@telegraphics.com.au>
Sun, 1 Jan 2017 00:56:26 +0000 (19:56 -0500)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 7 Feb 2017 05:56:21 +0000 (16:56 +1100)
When a read transaction completes, one of several things will happen:
a new transfer is started by the driver, a new transfer request
is raised by the Cuda (i.e. TREQ asserted), or both happen at once.

When both happen at once, there is a race condition between the TREQ test
in the read_done state and the same test in cuda_start(). Moreover, the
former test uses a stale TREQ value.

Theoretically, this can result in the undesirable outcome that the
interrupt handler completes with the state machine 'idle' when it should
instead start the next transaction.

Avoid this race by calling cuda_start() first and then confirming that it
succeeded. If not, test the current TREQ value before entering the
'reading' state.

Tested-by: Stan Johnson <userm57@yahoo.com>
Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
drivers/macintosh/via-cuda.c

index ae3da6b952295e36e4023b354e46b1736d9d8f41..32126958ac66bf56b42c59b1922d88dead22395b 100644 (file)
@@ -605,12 +605,11 @@ cuda_interrupt(int irq, void *arg)
            memcpy(ibuf, cuda_rbuf, ibuf_len);
        }
        reply_ptr = cuda_rbuf;
-       if (TREQ_asserted(status)) {
+       cuda_state = idle;
+       cuda_start();
+       if (cuda_state == idle && TREQ_asserted(in_8(&via[B]))) {
            assert_TIP();
            cuda_state = reading;
-       } else {
-           cuda_state = idle;
-           cuda_start();
        }
        break;