drm/nv50/kms: fix pin refcnt leaks
authorBen Skeggs <bskeggs@redhat.com>
Tue, 9 Jul 2013 02:35:55 +0000 (12:35 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Wed, 10 Jul 2013 00:47:42 +0000 (10:47 +1000)
Weren't critical previously, the buffers would go away anyway.  But with
recent changes to core drm/ttm lockdep will get pissed off now, so let's
fix it.

Reported-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Acked-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
drivers/gpu/drm/nouveau/nv50_display.c

index 54dc6355b0c2a02b7629440248a3cfb5e22a234c..8b40a36c1b57eb2f4e1eeac30a20478e7f563974 100644 (file)
@@ -355,6 +355,7 @@ struct nv50_oimm {
 
 struct nv50_head {
        struct nouveau_crtc base;
+       struct nouveau_bo *image;
        struct nv50_curs curs;
        struct nv50_sync sync;
        struct nv50_ovly ovly;
@@ -517,9 +518,10 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 {
        struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
        struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+       struct nv50_head *head = nv50_head(crtc);
        struct nv50_sync *sync = nv50_sync(crtc);
-       int head = nv_crtc->index, ret;
        u32 *push;
+       int ret;
 
        swap_interval <<= 4;
        if (swap_interval == 0)
@@ -537,7 +539,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                        return ret;
 
                BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2);
-               OUT_RING  (chan, NvEvoSema0 + head);
+               OUT_RING  (chan, NvEvoSema0 + nv_crtc->index);
                OUT_RING  (chan, sync->addr ^ 0x10);
                BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1);
                OUT_RING  (chan, sync->data + 1);
@@ -546,7 +548,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                OUT_RING  (chan, sync->data);
        } else
        if (chan && nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) {
-               u64 addr = nv84_fence_crtc(chan, head) + sync->addr;
+               u64 addr = nv84_fence_crtc(chan, nv_crtc->index) + sync->addr;
                ret = RING_SPACE(chan, 12);
                if (ret)
                        return ret;
@@ -565,7 +567,7 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL);
        } else
        if (chan) {
-               u64 addr = nv84_fence_crtc(chan, head) + sync->addr;
+               u64 addr = nv84_fence_crtc(chan, nv_crtc->index) + sync->addr;
                ret = RING_SPACE(chan, 10);
                if (ret)
                        return ret;
@@ -630,6 +632,8 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        evo_mthd(push, 0x0080, 1);
        evo_data(push, 0x00000000);
        evo_kick(push, sync);
+
+       nouveau_bo_ref(nv_fb->nvbo, &head->image);
        return 0;
 }
 
@@ -1038,18 +1042,17 @@ static int
 nv50_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
 {
        struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb);
+       struct nv50_head *head = nv50_head(crtc);
        int ret;
 
        ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM);
-       if (ret)
-               return ret;
-
-       if (old_fb) {
-               nvfb = nouveau_framebuffer(old_fb);
-               nouveau_bo_unpin(nvfb->nvbo);
+       if (ret == 0) {
+               if (head->image)
+                       nouveau_bo_unpin(head->image);
+               nouveau_bo_ref(nvfb->nvbo, &head->image);
        }
 
-       return 0;
+       return ret;
 }
 
 static int
@@ -1198,6 +1201,15 @@ nv50_crtc_lut_load(struct drm_crtc *crtc)
        }
 }
 
+static void
+nv50_crtc_disable(struct drm_crtc *crtc)
+{
+       struct nv50_head *head = nv50_head(crtc);
+       if (head->image)
+               nouveau_bo_unpin(head->image);
+       nouveau_bo_ref(NULL, &head->image);
+}
+
 static int
 nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
                     uint32_t handle, uint32_t width, uint32_t height)
@@ -1271,18 +1283,29 @@ nv50_crtc_destroy(struct drm_crtc *crtc)
        struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
        struct nv50_disp *disp = nv50_disp(crtc->dev);
        struct nv50_head *head = nv50_head(crtc);
+
        nv50_dmac_destroy(disp->core, &head->ovly.base);
        nv50_pioc_destroy(disp->core, &head->oimm.base);
        nv50_dmac_destroy(disp->core, &head->sync.base);
        nv50_pioc_destroy(disp->core, &head->curs.base);
+
+       /*XXX: this shouldn't be necessary, but the core doesn't call
+        *     disconnect() during the cleanup paths
+        */
+       if (head->image)
+               nouveau_bo_unpin(head->image);
+       nouveau_bo_ref(NULL, &head->image);
+
        nouveau_bo_unmap(nv_crtc->cursor.nvbo);
        if (nv_crtc->cursor.nvbo)
                nouveau_bo_unpin(nv_crtc->cursor.nvbo);
        nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
+
        nouveau_bo_unmap(nv_crtc->lut.nvbo);
        if (nv_crtc->lut.nvbo)
                nouveau_bo_unpin(nv_crtc->lut.nvbo);
        nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
+
        drm_crtc_cleanup(crtc);
        kfree(crtc);
 }
@@ -1296,6 +1319,7 @@ static const struct drm_crtc_helper_funcs nv50_crtc_hfunc = {
        .mode_set_base = nv50_crtc_mode_set_base,
        .mode_set_base_atomic = nv50_crtc_mode_set_base_atomic,
        .load_lut = nv50_crtc_lut_load,
+       .disable = nv50_crtc_disable,
 };
 
 static const struct drm_crtc_funcs nv50_crtc_func = {