gfx: drv: Fix race between SGX and page flip
authorPauli Nieminen <pauli.nieminen@linux.intel.com>
Thu, 1 Mar 2012 22:40:36 +0000 (00:40 +0200)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 3 Jul 2012 09:30:21 +0000 (12:30 +0300)
There is possible race condition where SGX might have parallel leads
happening while page flip is processing. There is small race condition
open where software managed sync object completion may block SGX from
completing rendering.

The deadlock situation would cause complete system deadlock which isn't
simple to recover from.

Issue: ANDROID-1661
Signed-off-by: Pauli Nieminen <pauli.nieminen@linux.intel.com>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
drivers/staging/mrst/drv/mdfld_overlay.c
drivers/staging/mrst/drv/psb_fb.c
drivers/staging/mrst/drv/psb_fb.h
drivers/staging/mrst/drv/psb_page_flip.c

index ac84ddb..c4e7f5d 100644 (file)
@@ -40,6 +40,9 @@
 #include "fp_trig.h"
 
 #include "drm_flip.h"
+#include "ossync.h"
+#include "mutex.h"
+#include "lock.h"
 
 enum {
        OVL_DIRTY_REGS  = 0x1,
@@ -1601,6 +1604,9 @@ struct mfld_overlay_flip {
        struct mfld_overlay_regs regs;
        bool vblank_ref;
        unsigned int dirty;
+       atomic_t refcnt;
+       struct psb_pending_values pending_values;
+       struct pvr_pending_sync pending_sync;
 };
 
 static void ovl_prepare(struct drm_flip *flip)
@@ -1657,6 +1663,35 @@ static bool ovl_flip_vblank(struct drm_flip *pending_flip)
        return (OVL_REG_READ(ovl, OVL_DOVSTA) & OVL_DOVSTA_OVR_UPDT) != 0;
 }
 
+static void free_flip(struct mfld_overlay_flip *oflip)
+{
+       if (atomic_dec_and_test(&oflip->refcnt))
+               kfree(oflip);
+}
+
+static void ovl_sync_callback(struct pvr_pending_sync *sync,
+               bool call_from_work)
+{
+       struct mfld_overlay_flip *oflip =
+               container_of(sync, struct mfld_overlay_flip, pending_sync);
+
+       if (psb_fb_increase_read_ops_completed(oflip->old_mem_info,
+                       &oflip->pending_values, sync)) {
+               WARN(true, "Sync callback called without completing operation");
+               return;
+       }
+
+       free_flip(oflip);
+
+       if (call_from_work)
+               mutex_lock(&gPVRSRVLock);
+
+       PVRSRVScheduleDeviceCallbacks();
+
+       if (call_from_work)
+               mutex_unlock(&gPVRSRVLock);
+}
+
 static void ovl_flip_complete(struct drm_flip *pending_flip)
 {
        struct mfld_overlay_flip *oflip =
@@ -1667,7 +1702,13 @@ static void ovl_flip_complete(struct drm_flip *pending_flip)
        if (oflip->vblank_ref)
                drm_vblank_put(dev, pipe);
 
-       psb_fb_increase_read_ops_completed(oflip->old_mem_info);
+       atomic_inc(&oflip->refcnt);
+       oflip->pending_sync.callback = ovl_sync_callback;
+       if (psb_fb_increase_read_ops_completed(oflip->old_mem_info,
+                               &oflip->pending_values, &oflip->pending_sync))
+               return;
+
+       free_flip(oflip);
        PVRSRVScheduleDeviceCallbacks();
 }
 
@@ -1689,7 +1730,7 @@ static void ovl_flip_cleanup(struct drm_flip *pending_flip)
        psb_fb_gtt_unref(dev, oflip->mem_info, oflip->tgid);
        mutex_unlock(&dev->mode_config.mutex);
 
-       kfree(oflip);
+       free_flip(oflip);
 }
 
 static const struct drm_flip_helper_funcs ovl_flip_funcs = {
@@ -1846,6 +1887,7 @@ struct drm_flip *mdfld_overlay_atomic_flip(struct drm_plane *plane, int pipe)
 
        drm_flip_init(&oflip->base, &ovl->flip_helper);
 
+       atomic_set(&oflip->refcnt, 1);
        oflip->mem_info = ovl->mem_info;
        /* reference ownership moved to the flip */
        ovl->mem_info = NULL;
@@ -1858,7 +1900,8 @@ struct drm_flip *mdfld_overlay_atomic_flip(struct drm_plane *plane, int pipe)
 
        oflip->vblank_ref = false;
 
-       psb_fb_increase_read_ops_pending(oflip->old_mem_info);
+       psb_fb_increase_read_ops_pending(oflip->old_mem_info,
+                       &oflip->pending_values);
        psb_fb_flip_trace(oflip->old_mem_info, oflip->mem_info);
 
        /* we must pipeline the register changes */
index 01c1b74..7ce5563 100644 (file)
@@ -45,6 +45,8 @@
 #include "mdfld_output.h"
 #include "pvr_trace_cmd.h"
 
+#include "ossync.h"
+
 extern struct mutex gPVRSRVLock;
 
 extern int MRSTLFBHandleChangeFB(struct drm_device* dev, struct psb_framebuffer *psbfb);
@@ -908,21 +910,47 @@ void psb_fb_gtt_unref(struct drm_device *dev,
 }
 
 void
-psb_fb_increase_read_ops_pending(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo)
+psb_fb_increase_read_ops_pending(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo,
+               struct psb_pending_values *pending)
 {
-       if (psKernelMemInfo && psKernelMemInfo->psKernelSyncInfo)
-               psKernelMemInfo->psKernelSyncInfo
-                       ->psSyncData->ui32ReadOpsPending++;
+       PVRSRV_SYNC_DATA *sync_data;
+       if (!psKernelMemInfo || !psKernelMemInfo->psKernelSyncInfo)
+               return;
+
+       sync_data = psKernelMemInfo->psKernelSyncInfo->psSyncData;
+       pending->write = sync_data->ui32WriteOpsPending;
+       pending->read = sync_data->ui32ReadOpsPending++;
 }
 
-void
-psb_fb_increase_read_ops_completed(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo)
+int
+psb_fb_increase_read_ops_completed(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo,
+               struct psb_pending_values *pending,
+               struct pvr_pending_sync *pending_sync)
 {
-       if (psKernelMemInfo && psKernelMemInfo->psKernelSyncInfo)
-               psKernelMemInfo->psKernelSyncInfo
-                       ->psSyncData->ui32ReadOpsComplete++;
+       PVRSRV_SYNC_DATA *sync_data;
+       if (!psKernelMemInfo || !psKernelMemInfo->psKernelSyncInfo)
+               return 0;
 
+       sync_data = psKernelMemInfo->psKernelSyncInfo->psSyncData;
+       if (unlikely(
+           pvr_ops_after(pending->write, sync_data->ui32WriteOpsComplete) ||
+           pvr_ops_after(pending->read, sync_data->ui32ReadOpsComplete))) {
+               /* Has to wait SGX micorkernel to complete parallel reads to
+                * avoid deadlock.
+                */
+
+               pending_sync->sync_info = psKernelMemInfo->psKernelSyncInfo;
+               pending_sync->pending_read_ops = pending->read;
+               pending_sync->pending_write_ops = pending->write;
+               pending_sync->flags = PVRSRV_SYNC_WRITE | PVRSRV_SYNC_READ;
+
+               PVRSRVCallbackOnSync2(pending_sync);
+
+               return -EBUSY;
+       }
+       sync_data->ui32ReadOpsComplete++;
        pvr_trcmd_check_syn_completions(PVR_TRCMD_FLPCOMP);
+       return 0;
 }
 
 void
index b085496..5528b5d 100644 (file)
@@ -48,6 +48,11 @@ struct psb_fbdev {
        struct psb_framebuffer * pfb;
 };
 
+struct psb_pending_values {
+       uint32_t read;
+       uint32_t write;
+};
+
 #define to_psb_fb(x) container_of(x, struct psb_framebuffer, base)
 
 
@@ -63,8 +68,13 @@ void psb_fb_gtt_unref(struct drm_device *dev,
                      PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo,
                      uint32_t tgid);
 
-void psb_fb_increase_read_ops_pending(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo);
-void psb_fb_increase_read_ops_completed(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo);
+struct pvr_pending_sync;
+
+void psb_fb_increase_read_ops_pending(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo,
+               struct psb_pending_values *pending);
+int psb_fb_increase_read_ops_completed(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo,
+               struct psb_pending_values *pending,
+               struct pvr_pending_sync *pending_sync);
 
 void psb_fb_flip_trace(PVRSRV_KERNEL_MEM_INFO *old,
                       PVRSRV_KERNEL_MEM_INFO *new);
index a05b840..116c623 100644 (file)
@@ -40,6 +40,8 @@
 #endif
 
 #include "drm_flip.h"
+#include "mutex.h"
+#include "lock.h"
 
 struct pending_flip {
        struct drm_crtc *crtc;
@@ -54,6 +56,8 @@ struct pending_flip {
        struct list_head companions;
        u32 tgid;
        bool vblank_ref;
+       atomic_t refcnt;
+       struct psb_pending_values pending_values;
 };
 
 static const u32 dspsurf_reg[] = {
@@ -238,6 +242,35 @@ static void psb_flip_driver_flush(struct drm_flip_driver *driver)
        (void)ioread32(dev_priv->vdc_reg + PIPEASTAT);
 }
 
+static void free_flip(struct pending_flip *crtc_flip)
+{
+       if (atomic_dec_and_test(&crtc_flip->refcnt))
+               kfree(crtc_flip);
+}
+
+static void psb_flip_complete_sync_callback(struct pvr_pending_sync *sync,
+               bool call_from_work)
+{
+       struct pending_flip *crtc_flip =
+               container_of(sync, struct pending_flip, pending_sync);
+
+       if (psb_fb_increase_read_ops_completed(crtc_flip->old_mem_info,
+                       &crtc_flip->pending_values, sync)) {
+               WARN(true, "Sync callback called without completing operation");
+               return;
+       }
+
+       free_flip(crtc_flip);
+
+       if (call_from_work)
+               mutex_lock(&gPVRSRVLock);
+
+       PVRSRVScheduleDeviceCallbacks();
+
+       if (call_from_work)
+               mutex_unlock(&gPVRSRVLock);
+}
+
 static void crtc_flip_complete(struct drm_flip *flip)
 {
        struct pending_flip *crtc_flip =
@@ -250,7 +283,14 @@ static void crtc_flip_complete(struct drm_flip *flip)
        if (crtc_flip->vblank_ref)
                drm_vblank_put(dev, pipe);
 
-       psb_fb_increase_read_ops_completed(crtc_flip->old_mem_info);
+       atomic_inc(&crtc_flip->refcnt);
+       crtc_flip->pending_sync.callback = psb_flip_complete_sync_callback;
+       if (psb_fb_increase_read_ops_completed(crtc_flip->old_mem_info,
+                               &crtc_flip->pending_values,
+                               &crtc_flip->pending_sync))
+               return;
+
+       free_flip(crtc_flip);
        PVRSRVScheduleDeviceCallbacks();
 }
 
@@ -507,6 +547,7 @@ psb_intel_crtc_page_flip(struct drm_crtc *crtc,
 
        drm_flip_init(&new_pending_flip->base, &to_psb_intel_crtc(crtc)->flip_helper);
 
+       atomic_set(&new_pending_flip->refcnt, 1);
        new_pending_flip->crtc = crtc;
        new_pending_flip->event = event;
        new_pending_flip->offset = psbfb->offset;
@@ -536,7 +577,8 @@ psb_intel_crtc_page_flip(struct drm_crtc *crtc,
        new_pending_flip->mem_info = psbfb->pvrBO;
        new_pending_flip->old_mem_info = current_fb_mem_info;
 
-       psb_fb_increase_read_ops_pending(current_fb_mem_info);
+       psb_fb_increase_read_ops_pending(current_fb_mem_info,
+                       &new_pending_flip->pending_values);
        psb_fb_flip_trace(current_fb_mem_info, psbfb->pvrBO);
 
        new_pending_flip->tgid = tgid;