drm/amdkfd: Fix kernel-queue wrapping bugs
authorYong Zhao <yong.zhao@amd.com>
Wed, 20 Sep 2017 22:10:21 +0000 (18:10 -0400)
committerOded Gabbay <oded.gabbay@gmail.com>
Wed, 20 Sep 2017 22:10:21 +0000 (18:10 -0400)
Avoid intermediate negative numbers when doing calculations with a mix
of signed and unsigned variables where implicit conversions can lead
to unexpected results.

When kernel queue buffer wraps around to 0, we need to check that rptr
won't be overwritten by the new packet.

Signed-off-by: Yong Zhao <yong.zhao@amd.com>
Signed-off-by: Felix Kuehling <Felix.Kuehling@amd.com>
Signed-off-by: Oded Gabbay <oded.gabbay@gmail.com>
drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c

index a4ca1133482cecbe2f6863034a082435b983a84d..ed71ad40e8f797ca3c7b7d4f129f5e9fda382d27 100644 (file)
@@ -210,6 +210,11 @@ static int acquire_packet_buffer(struct kernel_queue *kq,
        uint32_t wptr, rptr;
        unsigned int *queue_address;
 
+       /* When rptr == wptr, the buffer is empty.
+        * When rptr == wptr + 1, the buffer is full.
+        * It is always rptr that advances to the position of wptr, rather than
+        * the opposite. So we can only use up to queue_size_dwords - 1 dwords.
+        */
        rptr = *kq->rptr_kernel;
        wptr = *kq->wptr_kernel;
        queue_address = (unsigned int *)kq->pq_kernel_addr;
@@ -219,11 +224,10 @@ static int acquire_packet_buffer(struct kernel_queue *kq,
        pr_debug("wptr: %d\n", wptr);
        pr_debug("queue_address 0x%p\n", queue_address);
 
-       available_size = (rptr - 1 - wptr + queue_size_dwords) %
+       available_size = (rptr + queue_size_dwords - 1 - wptr) %
                                                        queue_size_dwords;
 
-       if (packet_size_in_dwords >= queue_size_dwords ||
-                       packet_size_in_dwords >= available_size) {
+       if (packet_size_in_dwords > available_size) {
                /*
                 * make sure calling functions know
                 * acquire_packet_buffer() failed
@@ -233,6 +237,14 @@ static int acquire_packet_buffer(struct kernel_queue *kq,
        }
 
        if (wptr + packet_size_in_dwords >= queue_size_dwords) {
+               /* make sure after rolling back to position 0, there is
+                * still enough space.
+                */
+               if (packet_size_in_dwords >= rptr) {
+                       *buffer_ptr = NULL;
+                       return -ENOMEM;
+               }
+               /* fill nops, roll back and start at position 0 */
                while (wptr > 0) {
                        queue_address[wptr] = kq->nop_packet;
                        wptr = (wptr + 1) % queue_size_dwords;