The attached patch tries to fix an issue with mismanagement of
graphics buffer eviction from memory, which prevents suspend/resume in
nouveau on one of my NVIDIA devices.  The issue likely affects Radeon
devices too, but I haven't been able to test that yet.

Would anyone like to try it and report whether (a) it breaks any
NVIDIA/Radeon graphics that worked without it, and whether (b) it
enables suspend/resume to work on any NVIDIA/Radeon devices?

You can test suspend/resume for just the graphics driver, rather than
suspending and resuming the whole system (which may fail for other
reasons), by trying

# drvctl -S nouveau0
# drvctl -R nouveaufb0

with NVIDIA devices, to suspend nouveau0 and all its descendants, and
then resume nouveaufb0 and all its ancestors; or

# drvctl -S radeon0
# drvctl -R radeondrmkmsfb0

with Radeon devices.
Index: sys/external/bsd/drm2/dist/drm/nouveau/nouveau_bo.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_bo.c,v
retrieving revision 1.6
diff -p -u -r1.6 nouveau_bo.c
--- sys/external/bsd/drm2/dist/drm/nouveau/nouveau_bo.c 29 Oct 2015 08:08:52 
-0000      1.6
+++ sys/external/bsd/drm2/dist/drm/nouveau/nouveau_bo.c 13 Apr 2016 22:10:28 
-0000
@@ -1524,6 +1524,16 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt 
 #endif
 }
 
+#ifdef __NetBSD__
+static void
+nouveau_ttm_tt_swapout(struct ttm_tt *ttm)
+{
+       struct ttm_dma_tt *ttm_dma = container_of(ttm, struct ttm_dma_tt, ttm);
+
+       ttm_bus_dma_swapout(ttm_dma);
+}
+#endif
+
 void
 nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence)
 {
@@ -1581,6 +1591,7 @@ struct ttm_bo_driver nouveau_bo_driver =
        .ttm_tt_populate = &nouveau_ttm_tt_populate,
        .ttm_tt_unpopulate = &nouveau_ttm_tt_unpopulate,
 #ifdef __NetBSD__
+       .ttm_tt_swapout = &nouveau_ttm_tt_swapout,
        .ttm_uvm_ops = &nouveau_uvm_ops,
 #endif
        .invalidate_caches = nouveau_bo_invalidate_caches,
Index: sys/external/bsd/drm2/dist/drm/nouveau/nouveau_chan.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_chan.c,v
retrieving revision 1.2
diff -p -u -r1.2 nouveau_chan.c
--- sys/external/bsd/drm2/dist/drm/nouveau/nouveau_chan.c       6 Aug 2014 
13:35:13 -0000       1.2
+++ sys/external/bsd/drm2/dist/drm/nouveau/nouveau_chan.c       13 Apr 2016 
22:10:28 -0000
@@ -59,12 +59,13 @@ nouveau_channel_idle(struct nouveau_chan
        ret = nouveau_fence_new(chan, false, &fence);
        if (!ret) {
                ret = nouveau_fence_wait(fence, false, false);
+               NV_ERROR(cli, "fence wait failed: %d\n", ret);
                nouveau_fence_unref(&fence);
        }
 
        if (ret)
-               NV_ERROR(cli, "failed to idle channel 0x%08x [%s]\n",
-                        chan->handle, cli->base.name);
+               NV_ERROR(cli, "failed to idle channel 0x%08x [%s] %d\n",
+                        chan->handle, cli->base.name, ret);
        return ret;
 }
 
Index: sys/external/bsd/drm2/dist/drm/radeon/radeon_ttm.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/radeon/radeon_ttm.c,v
retrieving revision 1.7
diff -p -u -r1.7 radeon_ttm.c
--- sys/external/bsd/drm2/dist/drm/radeon/radeon_ttm.c  10 Apr 2015 17:44:35 
-0000      1.7
+++ sys/external/bsd/drm2/dist/drm/radeon/radeon_ttm.c  13 Apr 2016 22:10:28 
-0000
@@ -716,6 +716,15 @@ static void radeon_ttm_tt_unpopulate(str
 }
 
 #ifdef __NetBSD__
+static void radeon_ttm_tt_swapout(struct ttm_tt *ttm)
+{
+       struct radeon_ttm_tt *gtt = container_of(ttm, struct radeon_ttm_tt,
+           ttm.ttm);
+       struct ttm_dma_tt *ttm_dma = &gtt->ttm;
+
+       ttm_bus_dma_swapout(ttm_dma);
+}
+
 static int     radeon_ttm_fault(struct uvm_faultinfo *, vaddr_t,
                    struct vm_page **, int, int, vm_prot_t, int);
 
@@ -731,6 +740,7 @@ static struct ttm_bo_driver radeon_bo_dr
        .ttm_tt_populate = &radeon_ttm_tt_populate,
        .ttm_tt_unpopulate = &radeon_ttm_tt_unpopulate,
 #ifdef __NetBSD__
+       .ttm_tt_swapout = &radeon_ttm_tt_swapout,
        .ttm_uvm_ops = &radeon_uvm_ops,
 #endif
        .invalidate_caches = &radeon_invalidate_caches,
Index: sys/external/bsd/drm2/dist/drm/ttm/ttm_bo.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/ttm/ttm_bo.c,v
retrieving revision 1.10
diff -p -u -r1.10 ttm_bo.c
--- sys/external/bsd/drm2/dist/drm/ttm/ttm_bo.c 20 Apr 2015 20:15:22 -0000      
1.10
+++ sys/external/bsd/drm2/dist/drm/ttm/ttm_bo.c 13 Apr 2016 22:10:28 -0000
@@ -319,8 +319,11 @@ static int ttm_bo_handle_move_mem(struct
        if (old_is_pci || new_is_pci ||
            ((mem->placement & bo->mem.placement & TTM_PL_MASK_CACHING) == 0)) {
                ret = ttm_mem_io_lock(old_man, true);
-               if (unlikely(ret != 0))
+               if (unlikely(ret != 0)) {
+                       printf("%s: ttm_mem_io_lock failed: %d\n", __func__,
+                           ret);
                        goto out_err;
+               }
                ttm_bo_unmap_virtual_locked(bo);
                ttm_mem_io_unlock(old_man);
        }
@@ -333,18 +336,27 @@ static int ttm_bo_handle_move_mem(struct
                if (bo->ttm == NULL) {
                        bool zero = !(old_man->flags & TTM_MEMTYPE_FLAG_FIXED);
                        ret = ttm_bo_add_ttm(bo, zero);
-                       if (ret)
+                       if (ret) {
+                               printf("%s: ttm_bo_add_ttm failed: %d\n",
+                                   __func__, ret);
                                goto out_err;
+                       }
                }
 
                ret = ttm_tt_set_placement_caching(bo->ttm, mem->placement);
-               if (ret)
+               if (ret) {
+                       printf("%s: ttm_tt_set_placement_caching failed: %d\n",
+                           __func__, ret);
                        goto out_err;
+               }
 
                if (mem->mem_type != TTM_PL_SYSTEM) {
                        ret = ttm_tt_bind(bo->ttm, mem);
-                       if (ret)
+                       if (ret) {
+                               printf("%s: ttm_tt_bind failed: %d\n",
+                                   __func__, ret);
                                goto out_err;
+                       }
                }
 
                if (bo->mem.mem_type == TTM_PL_SYSTEM) {
@@ -360,13 +372,23 @@ static int ttm_bo_handle_move_mem(struct
                bdev->driver->move_notify(bo, mem);
 
        if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) &&
-           !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED))
+           !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED)) {
                ret = ttm_bo_move_ttm(bo, evict, no_wait_gpu, mem);
-       else if (bdev->driver->move)
+               if (ret)
+                       printf("%s: ttm_bo_move_ttm failed: %d\n",
+                           __func__, ret);
+       } else if (bdev->driver->move) {
                ret = bdev->driver->move(bo, evict, interruptible,
                                         no_wait_gpu, mem);
-       else
+               if (ret)
+                       printf("%s: bdev->driver->move failed: %d\n",
+                           __func__, ret);
+       } else {
                ret = ttm_bo_move_memcpy(bo, evict, no_wait_gpu, mem);
+               if (ret)
+                       printf("%s: ttm_bo_move_memcpy failed: %d\n",
+                           __func__, ret);
+       }
 
        if (ret) {
                if (bdev->driver->move_notify) {
@@ -398,6 +420,7 @@ moved:
        } else
                bo->offset = 0;
 
+       printf("%s: success\n", __func__);
        return 0;
 
 out_err:
@@ -710,6 +733,7 @@ static int ttm_bo_evict(struct ttm_buffe
        spin_unlock(&bdev->fence_lock);
 
        if (unlikely(ret != 0)) {
+               printf("%s: ttm_bo_wait failed: %d\n", __func__, ret);
                if (ret != -ERESTARTSYS) {
                        pr_err("Failed to expire sync object before buffer 
eviction\n");
                }
@@ -731,6 +755,7 @@ static int ttm_bo_evict(struct ttm_buffe
        ret = ttm_bo_mem_space(bo, &placement, &evict_mem, interruptible,
                                no_wait_gpu);
        if (ret) {
+               printf("%s: ttm_bo_mem_space failed: %d\n", __func__, ret);
                if (ret != -ERESTARTSYS) {
                        pr_err("Failed to find memory space for buffer 0x%p 
eviction\n",
                               bo);
@@ -742,6 +767,8 @@ static int ttm_bo_evict(struct ttm_buffe
        ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, interruptible,
                                     no_wait_gpu);
        if (ret) {
+               printf("%s: ttm_bo_handle_move_mem failed: %d\n", __func__,
+                   ret);
                if (ret != -ERESTARTSYS)
                        pr_err("Buffer eviction failed\n");
                ttm_bo_mem_put(bo, &evict_mem);
@@ -765,6 +792,7 @@ static int ttm_mem_evict_first(struct tt
        spin_lock(&glob->lru_lock);
        list_for_each_entry(bo, &man->lru, lru) {
                ret = __ttm_bo_reserve(bo, false, true, false, 0);
+               printf("%s: reserve returned %d\n", __func__, ret);
                if (!ret)
                        break;
        }
@@ -779,6 +807,8 @@ static int ttm_mem_evict_first(struct tt
        if (!list_empty(&bo->ddestroy)) {
                ret = ttm_bo_cleanup_refs_and_unlock(bo, interruptible,
                                                     no_wait_gpu);
+               printf("%s: ttm_bo_cleanup_refs_and_unlock returned %d\n",
+                   __func__, ret);
                kref_put(&bo->list_kref, ttm_bo_release_list);
                return ret;
        }
@@ -791,6 +821,7 @@ static int ttm_mem_evict_first(struct tt
        ttm_bo_list_ref_sub(bo, put_count, true);
 
        ret = ttm_bo_evict(bo, interruptible, no_wait_gpu);
+       printf("%s: ttm_bo_evict returned %d\n", __func__, ret);
        ttm_bo_unreserve(bo);
 
        kref_put(&bo->list_kref, ttm_bo_release_list);
@@ -1296,6 +1327,8 @@ static int ttm_bo_force_list_clean(struc
                ret = ttm_mem_evict_first(bdev, mem_type, false, false);
                if (ret) {
                        if (allow_errors) {
+                               printf("%s: ttm_mem_evict_first failed: %d\n",
+                                   __func__, ret);
                                return ret;
                        } else {
                                pr_err("Cleanup eviction failed\n");
Index: sys/external/bsd/drm2/dist/drm/ttm/ttm_tt.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/ttm/ttm_tt.c,v
retrieving revision 1.6
diff -p -u -r1.6 ttm_tt.c
--- sys/external/bsd/drm2/dist/drm/ttm/ttm_tt.c 27 Jul 2014 00:40:39 -0000      
1.6
+++ sys/external/bsd/drm2/dist/drm/ttm/ttm_tt.c 13 Apr 2016 22:10:28 -0000
@@ -349,15 +349,27 @@ int ttm_tt_bind(struct ttm_tt *ttm, stru
 }
 EXPORT_SYMBOL(ttm_tt_bind);
 
-int ttm_tt_swapin(struct ttm_tt *ttm)
-{
 #ifdef __NetBSD__
+/*
+ * ttm_tt_wire(ttm)
+ *
+ *     Wire the uvm pages of ttm and fill the ttm page array.  ttm
+ *     must be unpopulated or unbound, and must be marked swapped.
+ *     This does not change either state -- the caller is expected to
+ *     include it among other operations for such a state transition.
+ */
+int
+ttm_tt_wire(struct ttm_tt *ttm)
+{
        struct uvm_object *uobj = ttm->swap_storage;
        struct vm_page *page;
        unsigned i;
        int error;
 
+       KASSERT(ttm->state == tt_unpopulated || ttm->state == tt_unbound);
+       KASSERT(ISSET(ttm->page_flags, TTM_PAGE_FLAG_SWAPPED));
        KASSERT(uobj != NULL);
+
        error = uvm_obj_wirepages(uobj, 0, (ttm->num_pages << PAGE_SHIFT),
            &ttm->pglist);
        if (error)
@@ -375,7 +387,35 @@ int ttm_tt_swapin(struct ttm_tt *ttm)
 
        /* Success!  */
        return 0;
-#else
+}
+
+/*
+ * ttm_tt_unwire(ttm)
+ *
+ *     Nullify the ttm page array and unwire the uvm pages of ttm.
+ *     ttm must be unbound and must be marked swapped.  This does not
+ *     change either state -- the caller is expected to include it
+ *     among other operations for such a state transition.
+ */
+void
+ttm_tt_unwire(struct ttm_tt *ttm)
+{
+       struct uvm_object *uobj = ttm->swap_storage;
+       unsigned i;
+
+       KASSERT(ttm->state == tt_unbound);
+       KASSERT(!ISSET(ttm->page_flags, TTM_PAGE_FLAG_SWAPPED));
+       KASSERT(uobj != NULL);
+
+       uvm_obj_unwirepages(uobj, 0, (ttm->num_pages << PAGE_SHIFT));
+       for (i = 0; i < ttm->num_pages; i++)
+               ttm->pages[i] = NULL;
+}
+#endif
+
+#ifndef __NetBSD__
+int ttm_tt_swapin(struct ttm_tt *ttm)
+{
        struct address_space *swap_space;
        struct file *swap_storage;
        struct page *from_page;
@@ -410,35 +450,20 @@ int ttm_tt_swapin(struct ttm_tt *ttm)
        return 0;
 out_err:
        return ret;
-#endif
 }
+#endif
 
-#ifdef __NetBSD__
 int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
 {
-       struct uvm_object *uobj = ttm->swap_storage;
-       unsigned i;
+#ifdef __NetBSD__
 
-       KASSERT((ttm->state == tt_unbound) || (ttm->state == tt_unpopulated));
+       KASSERT(ttm->state == tt_unpopulated || ttm->state == tt_unbound);
        KASSERT(ttm->caching_state == tt_cached);
-       KASSERT(uobj != NULL);
-
-       /*
-        * XXX Dunno what this persistent swap storage business is all
-        * about, but I see nothing using it and it doesn't make sense.
-        */
        KASSERT(persistent_swap_storage == NULL);
 
-       uvm_obj_unwirepages(uobj, 0, (ttm->num_pages << PAGE_SHIFT));
-       for (i = 0; i < ttm->num_pages; i++)
-               ttm->pages[i] = NULL;
-
-       /* Success!  */
+       ttm->bdev->driver->ttm_tt_swapout(ttm);
        return 0;
-}
 #else
-int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
-{
        struct address_space *swap_space;
        struct file *swap_storage;
        struct page *from_page;
@@ -489,8 +514,8 @@ out_err:
                fput(swap_storage);
 
        return ret;
-}
 #endif
+}
 
 static void ttm_tt_clear_mapping(struct ttm_tt *ttm)
 {
Index: sys/external/bsd/drm2/dist/include/drm/ttm/ttm_bo_driver.h
===================================================================
RCS file: 
/cvsroot/src/sys/external/bsd/drm2/dist/include/drm/ttm/ttm_bo_driver.h,v
retrieving revision 1.2
diff -p -u -r1.2 ttm_bo_driver.h
--- sys/external/bsd/drm2/dist/include/drm/ttm/ttm_bo_driver.h  16 Jul 2014 
20:59:57 -0000      1.2
+++ sys/external/bsd/drm2/dist/include/drm/ttm/ttm_bo_driver.h  13 Apr 2016 
22:10:28 -0000
@@ -368,6 +368,15 @@ struct ttm_bo_driver {
        void (*ttm_tt_unpopulate)(struct ttm_tt *ttm);
 
        /**
+        * ttm_tt_swapout
+        *
+        * @ttm: The struct ttm_tt to contain the backing pages.
+        *
+        * Deactivate all backing pages, but don't free them
+        */
+       void (*ttm_tt_swapout)(struct ttm_tt *ttm);
+
+       /**
         * struct ttm_bo_driver member invalidate_caches
         *
         * @bdev: the buffer object device.
@@ -666,6 +675,25 @@ extern void ttm_tt_destroy(struct ttm_tt
  */
 extern void ttm_tt_unbind(struct ttm_tt *ttm);
 
+#ifdef __NetBSD__
+/**
+ * ttm_tt_wire
+ *
+ * @ttm The struct ttm_tt.
+ *
+ * Wire the pages of a ttm_tt, allocating pages for it if necessary.
+ */
+extern int ttm_tt_wire(struct ttm_tt *ttm);
+
+/**
+ * ttm_tt_unwire
+ *
+ * @ttm The struct ttm_tt.
+ *
+ * Unwire the pages of a ttm_tt.
+ */
+extern void ttm_tt_unwire(struct ttm_tt *ttm);
+#else
 /**
  * ttm_tt_swapin:
  *
@@ -674,6 +702,7 @@ extern void ttm_tt_unbind(struct ttm_tt 
  * Swap in a previously swap out ttm_tt.
  */
 extern int ttm_tt_swapin(struct ttm_tt *ttm);
+#endif
 
 /**
  * ttm_tt_cache_flush:
Index: sys/external/bsd/drm2/include/drm/ttm/ttm_page_alloc.h
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/include/drm/ttm/ttm_page_alloc.h,v
retrieving revision 1.1
diff -p -u -r1.1 ttm_page_alloc.h
--- sys/external/bsd/drm2/include/drm/ttm/ttm_page_alloc.h      16 Jul 2014 
20:59:58 -0000      1.1
+++ sys/external/bsd/drm2/include/drm/ttm/ttm_page_alloc.h      13 Apr 2016 
22:10:28 -0000
@@ -37,6 +37,7 @@ struct ttm_mem_global;
 
 int    ttm_bus_dma_populate(struct ttm_dma_tt *);
 void   ttm_bus_dma_unpopulate(struct ttm_dma_tt *);
+void   ttm_bus_dma_swapout(struct ttm_dma_tt *);
 
 static inline int
 ttm_page_alloc_init(struct ttm_mem_global *glob __unused,
Index: sys/external/bsd/drm2/ttm/ttm_bus_dma.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/ttm/ttm_bus_dma.c,v
retrieving revision 1.1
diff -p -u -r1.1 ttm_bus_dma.c
--- sys/external/bsd/drm2/ttm/ttm_bus_dma.c     16 Jul 2014 20:59:58 -0000      
1.1
+++ sys/external/bsd/drm2/ttm/ttm_bus_dma.c     13 Apr 2016 22:10:29 -0000
@@ -40,17 +40,47 @@ __KERNEL_RCSID(0, "$NetBSD: ttm_bus_dma.
 #include <ttm/ttm_bo_driver.h>
 #include <ttm/ttm_page_alloc.h>
 
+/*
+ * ttm_bus_dma_populate(ttm_dma)
+ *
+ *     If ttm_dma is not already populated, wire its pages and load
+ *     its DMA map.  The wiring and loading are stable as long as the
+ *     associated bo is reserved.
+ *
+ *     Transitions from tt_unpopulated or tt_unbound to tt_unbound.
+ *     Marks as wired, a.k.a. !swapped.
+ */
 int
 ttm_bus_dma_populate(struct ttm_dma_tt *ttm_dma)
 {
        int ret;
 
-       /* If it's already populated, nothing to do.  */
-       if (ttm_dma->ttm.state != tt_unpopulated)
-               return 0;
+       KASSERT(ttm_dma->ttm.state != tt_bound);
 
-       /* Wire the pages, allocating them if necessary.  */
-       ret = ttm_tt_swapin(&ttm_dma->ttm);
+       /* Check the current state.  */
+       if (ttm_dma->ttm.state == tt_unbound) {
+               /*
+                * If it's populated, then if the pages are wired and
+                * loaded already, nothing to do.
+                */
+               if (!ISSET(ttm_dma->ttm.page_flags, TTM_PAGE_FLAG_SWAPPED)) {
+                       printf("%s: already wired\n", __func__);
+                       return 0;
+               }
+       } else if (ttm_dma->ttm.state == tt_unpopulated) {
+               /* If it's unpopulated, it can't be swapped.  */
+               KASSERT(!ISSET(ttm_dma->ttm.page_flags,
+                       TTM_PAGE_FLAG_SWAPPED));
+               /* Pretend it is now, for the sake of ttm_tt_wire.  */
+               ttm_dma->ttm.page_flags |= TTM_PAGE_FLAG_SWAPPED;
+               printf("%s: pretending swapped\n", __func__);
+       }
+       printf("%s: wiring and loading, state=%d swapped=%d\n", __func__,
+           (int)ttm_dma->ttm.state,
+           (int)!!(ttm_dma->ttm.page_flags & TTM_PAGE_FLAG_SWAPPED));
+
+       /* Wire the uvm pages and fill the ttm page array.  */
+       ret = ttm_tt_wire(&ttm_dma->ttm);
        if (ret)
                goto fail0;
 
@@ -62,34 +92,87 @@ ttm_bus_dma_populate(struct ttm_dma_tt *
        if (ret)
                goto fail1;
 
-       /* Success!  */
+       /* Mark it wired.  */
+       ttm_dma->ttm.page_flags &= ~TTM_PAGE_FLAG_SWAPPED;
+
+       /* Mark it populated but unbound.  */
        ttm_dma->ttm.state = tt_unbound;
+
+       /* Success!  */
        return 0;
 
 fail2: __unused
        bus_dmamap_unload(ttm_dma->ttm.bdev->dmat, ttm_dma->dma_address);
-fail1: ttm_tt_swapout(&ttm_dma->ttm, NULL);
+fail1: ttm_tt_unwire(&ttm_dma->ttm);
 fail0: KASSERT(ret);
        return ret;
 }
 
-void
-ttm_bus_dma_unpopulate(struct ttm_dma_tt *ttm_dma)
+static void
+ttm_bus_dma_put(struct ttm_dma_tt *ttm_dma, int flags)
 {
        struct uvm_object *const uobj = ttm_dma->ttm.swap_storage;
        const size_t size = (ttm_dma->ttm.num_pages << PAGE_SHIFT);
 
-       /* Unload the DMA map.  */
-       bus_dmamap_unload(ttm_dma->ttm.bdev->dmat, ttm_dma->dma_address);
-
-       /* Unwire the pages.  */
-       ttm_tt_swapout(&ttm_dma->ttm, NULL);
+       /*
+        * Can't be tt_bound -- still in use and needs to be removed
+        * from GPU page tables.  Can't be tt_unpopulated -- if it
+        * were, why are you hnadling this?  Hence tt_unbound.
+        */
+       KASSERT(ttm_dma->ttm.state == tt_unbound);
+
+       /* If pages are wired and loaded, unload and unwire them.  */
+       if (!ISSET(ttm_dma->ttm.page_flags, TTM_PAGE_FLAG_SWAPPED)) {
+               printf("%s: unloading and unwiring\n", __func__);
+               bus_dmamap_unload(ttm_dma->ttm.bdev->dmat,
+                   ttm_dma->dma_address);
+               ttm_tt_unwire(&ttm_dma->ttm);
+               ttm_dma->ttm.page_flags |= TTM_PAGE_FLAG_SWAPPED;
+       }
 
        /* We are using uvm_aobj, which had better have a pgo_put.  */
        KASSERT(uobj->pgops->pgo_put);
 
-       /* Release the pages.  */
+       /* Release or deactivate the pages.  */
        mutex_enter(uobj->vmobjlock);
-       (void)(*uobj->pgops->pgo_put)(uobj, 0, size, PGO_CLEANIT|PGO_FREE);
+       (void)(*uobj->pgops->pgo_put)(uobj, 0, size, flags);
        /* pgo_put unlocks uobj->vmobjlock.  */
+
+       /* Mark it unpopulated.  */
+       ttm_dma->ttm.state = tt_unpopulated;
+}
+
+/*
+ * ttmm_bus_dma_unpopulate(ttm_dma)
+ *
+ *     Unload any DMA map, unwire any pages, and release any pages
+ *     associated with ttm_dma.
+ *
+ *     Transitions from tt_unbound to tt_unpopulated.  Marks as
+ *     unwired, a.k.a. swapped.
+ */
+void
+ttm_bus_dma_unpopulate(struct ttm_dma_tt *ttm_dma)
+{
+
+       printf("%s\n", __func__);
+       ttm_bus_dma_put(ttm_dma, PGO_CLEANIT|PGO_FREE);
+}
+
+/*
+ * ttm_bus_dma_swapout(ttm_dma)
+ *
+ *     Unload any DMA map, unwire any pages, and deactivate any pages
+ *     associated with ttm_dma so that they can be swapped out, but
+ *     don't release them.
+ *
+ *     Transitions from tt_unbound to tt_unpopulated.  Marks as
+ *     unwired, a.k.a. swapped.
+ */
+void
+ttm_bus_dma_swapout(struct ttm_dma_tt *ttm_dma)
+{
+
+       printf("%s\n", __func__);
+       ttm_bus_dma_put(ttm_dma, PGO_DEACTIVATE);
 }
Index: sys/external/bsd/drm2/dist/drm/nouveau/nouveau_bo.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_bo.c,v
retrieving revision 1.6
diff -p -u -r1.6 nouveau_bo.c
--- sys/external/bsd/drm2/dist/drm/nouveau/nouveau_bo.c 29 Oct 2015 08:08:52 
-0000      1.6
+++ sys/external/bsd/drm2/dist/drm/nouveau/nouveau_bo.c 16 Apr 2016 18:59:57 
-0000
@@ -1524,6 +1524,16 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt 
 #endif
 }
 
+#ifdef __NetBSD__
+static void
+nouveau_ttm_tt_swapout(struct ttm_tt *ttm)
+{
+       struct ttm_dma_tt *ttm_dma = container_of(ttm, struct ttm_dma_tt, ttm);
+
+       ttm_bus_dma_swapout(ttm_dma);
+}
+#endif
+
 void
 nouveau_bo_fence(struct nouveau_bo *nvbo, struct nouveau_fence *fence)
 {
@@ -1581,6 +1591,7 @@ struct ttm_bo_driver nouveau_bo_driver =
        .ttm_tt_populate = &nouveau_ttm_tt_populate,
        .ttm_tt_unpopulate = &nouveau_ttm_tt_unpopulate,
 #ifdef __NetBSD__
+       .ttm_tt_swapout = &nouveau_ttm_tt_swapout,
        .ttm_uvm_ops = &nouveau_uvm_ops,
 #endif
        .invalidate_caches = nouveau_bo_invalidate_caches,
Index: sys/external/bsd/drm2/dist/drm/radeon/radeon_ttm.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/radeon/radeon_ttm.c,v
retrieving revision 1.7
diff -p -u -r1.7 radeon_ttm.c
--- sys/external/bsd/drm2/dist/drm/radeon/radeon_ttm.c  10 Apr 2015 17:44:35 
-0000      1.7
+++ sys/external/bsd/drm2/dist/drm/radeon/radeon_ttm.c  16 Apr 2016 18:59:57 
-0000
@@ -716,6 +716,15 @@ static void radeon_ttm_tt_unpopulate(str
 }
 
 #ifdef __NetBSD__
+static void radeon_ttm_tt_swapout(struct ttm_tt *ttm)
+{
+       struct radeon_ttm_tt *gtt = container_of(ttm, struct radeon_ttm_tt,
+           ttm.ttm);
+       struct ttm_dma_tt *ttm_dma = &gtt->ttm;
+
+       ttm_bus_dma_swapout(ttm_dma);
+}
+
 static int     radeon_ttm_fault(struct uvm_faultinfo *, vaddr_t,
                    struct vm_page **, int, int, vm_prot_t, int);
 
@@ -731,6 +740,7 @@ static struct ttm_bo_driver radeon_bo_dr
        .ttm_tt_populate = &radeon_ttm_tt_populate,
        .ttm_tt_unpopulate = &radeon_ttm_tt_unpopulate,
 #ifdef __NetBSD__
+       .ttm_tt_swapout = &radeon_ttm_tt_swapout,
        .ttm_uvm_ops = &radeon_uvm_ops,
 #endif
        .invalidate_caches = &radeon_invalidate_caches,
Index: sys/external/bsd/drm2/dist/drm/ttm/ttm_tt.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/dist/drm/ttm/ttm_tt.c,v
retrieving revision 1.6
diff -p -u -r1.6 ttm_tt.c
--- sys/external/bsd/drm2/dist/drm/ttm/ttm_tt.c 27 Jul 2014 00:40:39 -0000      
1.6
+++ sys/external/bsd/drm2/dist/drm/ttm/ttm_tt.c 16 Apr 2016 18:59:57 -0000
@@ -349,15 +349,30 @@ int ttm_tt_bind(struct ttm_tt *ttm, stru
 }
 EXPORT_SYMBOL(ttm_tt_bind);
 
-int ttm_tt_swapin(struct ttm_tt *ttm)
-{
 #ifdef __NetBSD__
+/*
+ * ttm_tt_wire(ttm)
+ *
+ *     Wire the uvm pages of ttm and fill the ttm page array.  ttm
+ *     must be unpopulated or unbound, and must be marked swapped.
+ *     This does not change either state -- the caller is expected to
+ *     include it among other operations for such a state transition.
+ */
+int
+ttm_tt_wire(struct ttm_tt *ttm)
+{
        struct uvm_object *uobj = ttm->swap_storage;
        struct vm_page *page;
        unsigned i;
        int error;
 
+       KASSERTMSG((ttm->state == tt_unpopulated || ttm->state == tt_unbound),
+           "ttm_tt %p must be unpopulated or unbound for wiring,"
+           " but state=%d",
+           ttm, (int)ttm->state);
+       KASSERT(ISSET(ttm->page_flags, TTM_PAGE_FLAG_SWAPPED));
        KASSERT(uobj != NULL);
+
        error = uvm_obj_wirepages(uobj, 0, (ttm->num_pages << PAGE_SHIFT),
            &ttm->pglist);
        if (error)
@@ -375,7 +390,37 @@ int ttm_tt_swapin(struct ttm_tt *ttm)
 
        /* Success!  */
        return 0;
-#else
+}
+
+/*
+ * ttm_tt_unwire(ttm)
+ *
+ *     Nullify the ttm page array and unwire the uvm pages of ttm.
+ *     ttm must be unbound and must be marked swapped.  This does not
+ *     change either state -- the caller is expected to include it
+ *     among other operations for such a state transition.
+ */
+void
+ttm_tt_unwire(struct ttm_tt *ttm)
+{
+       struct uvm_object *uobj = ttm->swap_storage;
+       unsigned i;
+
+       KASSERTMSG((ttm->state == tt_unbound),
+           "ttm_tt %p must be unbound for unwiring, but state=%d",
+           ttm, (int)ttm->state);
+       KASSERT(!ISSET(ttm->page_flags, TTM_PAGE_FLAG_SWAPPED));
+       KASSERT(uobj != NULL);
+
+       uvm_obj_unwirepages(uobj, 0, (ttm->num_pages << PAGE_SHIFT));
+       for (i = 0; i < ttm->num_pages; i++)
+               ttm->pages[i] = NULL;
+}
+#endif
+
+#ifndef __NetBSD__
+int ttm_tt_swapin(struct ttm_tt *ttm)
+{
        struct address_space *swap_space;
        struct file *swap_storage;
        struct page *from_page;
@@ -410,35 +455,25 @@ int ttm_tt_swapin(struct ttm_tt *ttm)
        return 0;
 out_err:
        return ret;
-#endif
 }
+#endif
 
-#ifdef __NetBSD__
 int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
 {
-       struct uvm_object *uobj = ttm->swap_storage;
-       unsigned i;
-
-       KASSERT((ttm->state == tt_unbound) || (ttm->state == tt_unpopulated));
-       KASSERT(ttm->caching_state == tt_cached);
-       KASSERT(uobj != NULL);
+#ifdef __NetBSD__
 
-       /*
-        * XXX Dunno what this persistent swap storage business is all
-        * about, but I see nothing using it and it doesn't make sense.
-        */
+       KASSERTMSG((ttm->state == tt_unpopulated || ttm->state == tt_unbound),
+           "ttm_tt %p must be unpopulated or unbound for swapout,"
+           " but state=%d",
+           ttm, (int)ttm->state);
+       KASSERTMSG((ttm->caching_state == tt_cached),
+           "ttm_tt %p must be cached for swapout, but caching_state=%d",
+           ttm, (int)ttm->caching_state);
        KASSERT(persistent_swap_storage == NULL);
 
-       uvm_obj_unwirepages(uobj, 0, (ttm->num_pages << PAGE_SHIFT));
-       for (i = 0; i < ttm->num_pages; i++)
-               ttm->pages[i] = NULL;
-
-       /* Success!  */
+       ttm->bdev->driver->ttm_tt_swapout(ttm);
        return 0;
-}
 #else
-int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
-{
        struct address_space *swap_space;
        struct file *swap_storage;
        struct page *from_page;
@@ -489,8 +524,8 @@ out_err:
                fput(swap_storage);
 
        return ret;
-}
 #endif
+}
 
 static void ttm_tt_clear_mapping(struct ttm_tt *ttm)
 {
Index: sys/external/bsd/drm2/dist/include/drm/ttm/ttm_bo_driver.h
===================================================================
RCS file: 
/cvsroot/src/sys/external/bsd/drm2/dist/include/drm/ttm/ttm_bo_driver.h,v
retrieving revision 1.2
diff -p -u -r1.2 ttm_bo_driver.h
--- sys/external/bsd/drm2/dist/include/drm/ttm/ttm_bo_driver.h  16 Jul 2014 
20:59:57 -0000      1.2
+++ sys/external/bsd/drm2/dist/include/drm/ttm/ttm_bo_driver.h  16 Apr 2016 
18:59:57 -0000
@@ -368,6 +368,15 @@ struct ttm_bo_driver {
        void (*ttm_tt_unpopulate)(struct ttm_tt *ttm);
 
        /**
+        * ttm_tt_swapout
+        *
+        * @ttm: The struct ttm_tt to contain the backing pages.
+        *
+        * Deactivate all backing pages, but don't free them
+        */
+       void (*ttm_tt_swapout)(struct ttm_tt *ttm);
+
+       /**
         * struct ttm_bo_driver member invalidate_caches
         *
         * @bdev: the buffer object device.
@@ -666,6 +675,25 @@ extern void ttm_tt_destroy(struct ttm_tt
  */
 extern void ttm_tt_unbind(struct ttm_tt *ttm);
 
+#ifdef __NetBSD__
+/**
+ * ttm_tt_wire
+ *
+ * @ttm The struct ttm_tt.
+ *
+ * Wire the pages of a ttm_tt, allocating pages for it if necessary.
+ */
+extern int ttm_tt_wire(struct ttm_tt *ttm);
+
+/**
+ * ttm_tt_unwire
+ *
+ * @ttm The struct ttm_tt.
+ *
+ * Unwire the pages of a ttm_tt.
+ */
+extern void ttm_tt_unwire(struct ttm_tt *ttm);
+#else
 /**
  * ttm_tt_swapin:
  *
@@ -674,6 +702,7 @@ extern void ttm_tt_unbind(struct ttm_tt 
  * Swap in a previously swap out ttm_tt.
  */
 extern int ttm_tt_swapin(struct ttm_tt *ttm);
+#endif
 
 /**
  * ttm_tt_cache_flush:
Index: sys/external/bsd/drm2/include/drm/ttm/ttm_page_alloc.h
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/include/drm/ttm/ttm_page_alloc.h,v
retrieving revision 1.1
diff -p -u -r1.1 ttm_page_alloc.h
--- sys/external/bsd/drm2/include/drm/ttm/ttm_page_alloc.h      16 Jul 2014 
20:59:58 -0000      1.1
+++ sys/external/bsd/drm2/include/drm/ttm/ttm_page_alloc.h      16 Apr 2016 
18:59:57 -0000
@@ -37,6 +37,7 @@ struct ttm_mem_global;
 
 int    ttm_bus_dma_populate(struct ttm_dma_tt *);
 void   ttm_bus_dma_unpopulate(struct ttm_dma_tt *);
+void   ttm_bus_dma_swapout(struct ttm_dma_tt *);
 
 static inline int
 ttm_page_alloc_init(struct ttm_mem_global *glob __unused,
Index: sys/external/bsd/drm2/ttm/ttm_bus_dma.c
===================================================================
RCS file: /cvsroot/src/sys/external/bsd/drm2/ttm/ttm_bus_dma.c,v
retrieving revision 1.1
diff -p -u -r1.1 ttm_bus_dma.c
--- sys/external/bsd/drm2/ttm/ttm_bus_dma.c     16 Jul 2014 20:59:58 -0000      
1.1
+++ sys/external/bsd/drm2/ttm/ttm_bus_dma.c     16 Apr 2016 18:59:57 -0000
@@ -40,17 +40,41 @@ __KERNEL_RCSID(0, "$NetBSD: ttm_bus_dma.
 #include <ttm/ttm_bo_driver.h>
 #include <ttm/ttm_page_alloc.h>
 
+/*
+ * ttm_bus_dma_populate(ttm_dma)
+ *
+ *     If ttm_dma is not already populated, wire its pages and load
+ *     its DMA map.  The wiring and loading are stable as long as the
+ *     associated bo is reserved.
+ *
+ *     Transitions from tt_unpopulated or tt_unbound to tt_unbound.
+ *     Marks as wired, a.k.a. !swapped.
+ */
 int
 ttm_bus_dma_populate(struct ttm_dma_tt *ttm_dma)
 {
        int ret;
 
-       /* If it's already populated, nothing to do.  */
-       if (ttm_dma->ttm.state != tt_unpopulated)
-               return 0;
+       KASSERT(ttm_dma->ttm.state != tt_bound);
 
-       /* Wire the pages, allocating them if necessary.  */
-       ret = ttm_tt_swapin(&ttm_dma->ttm);
+       /* Check the current state.  */
+       if (ttm_dma->ttm.state == tt_unbound) {
+               /*
+                * If it's populated, then if the pages are wired and
+                * loaded already, nothing to do.
+                */
+               if (!ISSET(ttm_dma->ttm.page_flags, TTM_PAGE_FLAG_SWAPPED))
+                       return 0;
+       } else if (ttm_dma->ttm.state == tt_unpopulated) {
+               /* If it's unpopulated, it can't be swapped.  */
+               KASSERT(!ISSET(ttm_dma->ttm.page_flags,
+                       TTM_PAGE_FLAG_SWAPPED));
+               /* Pretend it is now, for the sake of ttm_tt_wire.  */
+               ttm_dma->ttm.page_flags |= TTM_PAGE_FLAG_SWAPPED;
+       }
+
+       /* Wire the uvm pages and fill the ttm page array.  */
+       ret = ttm_tt_wire(&ttm_dma->ttm);
        if (ret)
                goto fail0;
 
@@ -62,34 +86,86 @@ ttm_bus_dma_populate(struct ttm_dma_tt *
        if (ret)
                goto fail1;
 
-       /* Success!  */
+       /* Mark it wired.  */
+       ttm_dma->ttm.page_flags &= ~TTM_PAGE_FLAG_SWAPPED;
+
+       /* Mark it populated but unbound.  */
        ttm_dma->ttm.state = tt_unbound;
+
+       /* Success!  */
        return 0;
 
 fail2: __unused
        bus_dmamap_unload(ttm_dma->ttm.bdev->dmat, ttm_dma->dma_address);
-fail1: ttm_tt_swapout(&ttm_dma->ttm, NULL);
+fail1: ttm_tt_unwire(&ttm_dma->ttm);
 fail0: KASSERT(ret);
        return ret;
 }
 
-void
-ttm_bus_dma_unpopulate(struct ttm_dma_tt *ttm_dma)
+static void
+ttm_bus_dma_put(struct ttm_dma_tt *ttm_dma, int flags)
 {
        struct uvm_object *const uobj = ttm_dma->ttm.swap_storage;
        const size_t size = (ttm_dma->ttm.num_pages << PAGE_SHIFT);
 
-       /* Unload the DMA map.  */
-       bus_dmamap_unload(ttm_dma->ttm.bdev->dmat, ttm_dma->dma_address);
-
-       /* Unwire the pages.  */
-       ttm_tt_swapout(&ttm_dma->ttm, NULL);
+       /*
+        * Can't be tt_bound -- still in use and needs to be removed
+        * from GPU page tables.  Can't be tt_unpopulated -- if it
+        * were, why are you hnadling this?  Hence tt_unbound.
+        */
+       KASSERTMSG((ttm_dma->ttm.state == tt_unbound),
+           "ttm_tt %p in invalid state for unpopulate/swapout: %d",
+           &ttm_dma->ttm, (int)ttm_dma->ttm.state);
+
+       /* If pages are wired and loaded, unload and unwire them.  */
+       if (!ISSET(ttm_dma->ttm.page_flags, TTM_PAGE_FLAG_SWAPPED)) {
+               bus_dmamap_unload(ttm_dma->ttm.bdev->dmat,
+                   ttm_dma->dma_address);
+               ttm_tt_unwire(&ttm_dma->ttm);
+               ttm_dma->ttm.page_flags |= TTM_PAGE_FLAG_SWAPPED;
+       }
 
        /* We are using uvm_aobj, which had better have a pgo_put.  */
        KASSERT(uobj->pgops->pgo_put);
 
-       /* Release the pages.  */
+       /* Release or deactivate the pages.  */
        mutex_enter(uobj->vmobjlock);
-       (void)(*uobj->pgops->pgo_put)(uobj, 0, size, PGO_CLEANIT|PGO_FREE);
+       (void)(*uobj->pgops->pgo_put)(uobj, 0, size, flags);
        /* pgo_put unlocks uobj->vmobjlock.  */
+
+       /* Mark it unpopulated.  */
+       ttm_dma->ttm.state = tt_unpopulated;
+}
+
+/*
+ * ttmm_bus_dma_unpopulate(ttm_dma)
+ *
+ *     Unload any DMA map, unwire any pages, and release any pages
+ *     associated with ttm_dma.
+ *
+ *     Transitions from tt_unbound to tt_unpopulated.  Marks as
+ *     unwired, a.k.a. swapped.
+ */
+void
+ttm_bus_dma_unpopulate(struct ttm_dma_tt *ttm_dma)
+{
+
+       ttm_bus_dma_put(ttm_dma, PGO_CLEANIT|PGO_FREE);
+}
+
+/*
+ * ttm_bus_dma_swapout(ttm_dma)
+ *
+ *     Unload any DMA map, unwire any pages, and deactivate any pages
+ *     associated with ttm_dma so that they can be swapped out, but
+ *     don't release them.
+ *
+ *     Transitions from tt_unbound to tt_unpopulated.  Marks as
+ *     unwired, a.k.a. swapped.
+ */
+void
+ttm_bus_dma_swapout(struct ttm_dma_tt *ttm_dma)
+{
+
+       ttm_bus_dma_put(ttm_dma, PGO_DEACTIVATE);
 }

Reply via email to