gfx: drv: Fix unlikely race condition
authorPauli Nieminen <pauli.nieminen@linux.intel.com>
Thu, 1 Mar 2012 22:40:33 +0000 (00:40 +0200)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 3 Jul 2012 09:30:20 +0000 (12:30 +0300)
Checking operation completeness before taking the lock may result to
state where IRQ work is run between checking for completion and before
the callback is added to the list. In practice that race will require
premption because time between microkernel modifying synchronization
object and kernel work check for status is many microseconds.

That could block the last page flip in the animation until next GPU
rendering is pushed from userspace. The missing frame in end of
animation could be extremy visible in some cases.

Signed-off-by: Pauli Nieminen <pauli.nieminen@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
drivers/staging/mrst/pvr/services4/srvkm/env/linux/ossync.c

index afdb152..b5dfa0e 100644 (file)
@@ -57,6 +57,7 @@ PVRSRVCallbackOnSync(PVRSRV_KERNEL_SYNC_INFO *sync_info,
                     pvr_sync_callback callback,
                     struct pvr_pending_sync *pending_sync)
 {
+       bool complete = false;
        u32 pending_read_ops = sync_info->psSyncData->ui32ReadOpsPending;
        u32 pending_write_ops = sync_info->psSyncData->ui32WriteOpsPending;
 
@@ -66,18 +67,19 @@ PVRSRVCallbackOnSync(PVRSRV_KERNEL_SYNC_INFO *sync_info,
        pending_sync->flags = flags;
        pending_sync->callback = callback;
 
+       spin_lock_irq(&sync_lock);
        /* If the object is already in sync, don't add it to the list */
-       if (pending_ops_completed(sync_info, flags,
+       if (!pending_ops_completed(sync_info, flags,
                                  pending_read_ops,
-                                 pending_write_ops)) {
-               callback(pending_sync, false);
-               return;
-       }
+                                 pending_write_ops))
+               list_add_tail(&pending_sync->list, &sync_list);
+       else
+               complete = true;
 
-       spin_lock_irq(&sync_lock);
-       list_add_tail(&pending_sync->list, &sync_list);
        spin_unlock_irq(&sync_lock);
 
+       if (complete)
+               callback(pending_sync, false);
        return;
 }