xref: /dragonfly/sys/dev/drm/ttm/ttm_bo_vm.c (revision 04b45e6f)
15718399fSFrançois Tigeot /**************************************************************************
25718399fSFrançois Tigeot  *
35718399fSFrançois Tigeot  * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
45718399fSFrançois Tigeot  * All Rights Reserved.
55718399fSFrançois Tigeot  *
65718399fSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
75718399fSFrançois Tigeot  * copy of this software and associated documentation files (the
85718399fSFrançois Tigeot  * "Software"), to deal in the Software without restriction, including
95718399fSFrançois Tigeot  * without limitation the rights to use, copy, modify, merge, publish,
105718399fSFrançois Tigeot  * distribute, sub license, and/or sell copies of the Software, and to
115718399fSFrançois Tigeot  * permit persons to whom the Software is furnished to do so, subject to
125718399fSFrançois Tigeot  * the following conditions:
135718399fSFrançois Tigeot  *
145718399fSFrançois Tigeot  * The above copyright notice and this permission notice (including the
155718399fSFrançois Tigeot  * next paragraph) shall be included in all copies or substantial portions
165718399fSFrançois Tigeot  * of the Software.
175718399fSFrançois Tigeot  *
185718399fSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
195718399fSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
205718399fSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
215718399fSFrançois Tigeot  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
225718399fSFrançois Tigeot  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
235718399fSFrançois Tigeot  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
245718399fSFrançois Tigeot  * USE OR OTHER DEALINGS IN THE SOFTWARE.
255718399fSFrançois Tigeot  *
265718399fSFrançois Tigeot  **************************************************************************/
275718399fSFrançois Tigeot /*
285718399fSFrançois Tigeot  * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
295718399fSFrançois Tigeot  */
305718399fSFrançois Tigeot /*
315718399fSFrançois Tigeot  * Copyright (c) 2013 The FreeBSD Foundation
325718399fSFrançois Tigeot  * All rights reserved.
335718399fSFrançois Tigeot  *
345718399fSFrançois Tigeot  * Portions of this software were developed by Konstantin Belousov
355718399fSFrançois Tigeot  * <kib@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
365718399fSFrançois Tigeot  *
375718399fSFrançois Tigeot  *$FreeBSD: head/sys/dev/drm2/ttm/ttm_bo_vm.c 253710 2013-07-27 16:44:37Z kib $
385718399fSFrançois Tigeot  */
395718399fSFrançois Tigeot 
405718399fSFrançois Tigeot #include "opt_vm.h"
415718399fSFrançois Tigeot 
42c66857ebSFrançois Tigeot #define pr_fmt(fmt) "[TTM] " fmt
43c66857ebSFrançois Tigeot 
44216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_module.h>
45216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_bo_driver.h>
46216f7a2cSFrançois Tigeot #include <drm/ttm/ttm_placement.h>
475718399fSFrançois Tigeot #include <vm/vm.h>
485718399fSFrançois Tigeot #include <vm/vm_page.h>
49797013cfSFrançois Tigeot #include <linux/errno.h>
50c66857ebSFrançois Tigeot #include <linux/export.h>
51ddea3d8dSzrj 
52ddea3d8dSzrj RB_GENERATE(ttm_bo_device_buffer_objects, ttm_buffer_object, vm_rb,
53ddea3d8dSzrj     ttm_bo_cmp_rb_tree_items);
54ddea3d8dSzrj 
555718399fSFrançois Tigeot 
565718399fSFrançois Tigeot #define TTM_BO_VM_NUM_PREFAULT 16
575718399fSFrançois Tigeot 
58ddea3d8dSzrj int
59ddea3d8dSzrj ttm_bo_cmp_rb_tree_items(struct ttm_buffer_object *a,
60ddea3d8dSzrj     struct ttm_buffer_object *b)
61ddea3d8dSzrj {
62ddea3d8dSzrj     if (a->vm_node->start < b->vm_node->start) {
63ddea3d8dSzrj         return (-1);
64ddea3d8dSzrj     } else if (a->vm_node->start > b->vm_node->start) {
65ddea3d8dSzrj         return (1);
66ddea3d8dSzrj     } else {
67ddea3d8dSzrj         return (0);
68ddea3d8dSzrj     }
69ddea3d8dSzrj }
70ddea3d8dSzrj 
71ddea3d8dSzrj 
725718399fSFrançois Tigeot static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev,
735718399fSFrançois Tigeot 						     unsigned long page_start,
745718399fSFrançois Tigeot 						     unsigned long num_pages)
755718399fSFrançois Tigeot {
765718399fSFrançois Tigeot 	unsigned long cur_offset;
775718399fSFrançois Tigeot 	struct ttm_buffer_object *bo;
785718399fSFrançois Tigeot 	struct ttm_buffer_object *best_bo = NULL;
795718399fSFrançois Tigeot 
80ddea3d8dSzrj 	bo = RB_ROOT(&bdev->addr_space_rb);
81ddea3d8dSzrj 	while (bo != NULL) {
825718399fSFrançois Tigeot 		cur_offset = bo->vm_node->start;
835718399fSFrançois Tigeot 		if (page_start >= cur_offset) {
845718399fSFrançois Tigeot 			best_bo = bo;
855718399fSFrançois Tigeot 			if (page_start == cur_offset)
865718399fSFrançois Tigeot 				break;
87ddea3d8dSzrj 			bo = RB_RIGHT(bo, vm_rb);
88c66857ebSFrançois Tigeot 		} else
89ddea3d8dSzrj 			bo = RB_LEFT(bo, vm_rb);
905718399fSFrançois Tigeot 	}
915718399fSFrançois Tigeot 
925718399fSFrançois Tigeot 	if (unlikely(best_bo == NULL))
935718399fSFrançois Tigeot 		return NULL;
945718399fSFrançois Tigeot 
955718399fSFrançois Tigeot 	if (unlikely((best_bo->vm_node->start + best_bo->num_pages) <
965718399fSFrançois Tigeot 		     (page_start + num_pages)))
975718399fSFrançois Tigeot 		return NULL;
985718399fSFrançois Tigeot 
995718399fSFrançois Tigeot 	return best_bo;
1005718399fSFrançois Tigeot }
1015718399fSFrançois Tigeot 
1025718399fSFrançois Tigeot static int
1035718399fSFrançois Tigeot ttm_bo_vm_fault(vm_object_t vm_obj, vm_ooffset_t offset,
1045718399fSFrançois Tigeot     int prot, vm_page_t *mres)
1055718399fSFrançois Tigeot {
1065718399fSFrançois Tigeot 	struct ttm_buffer_object *bo = vm_obj->handle;
1075718399fSFrançois Tigeot 	struct ttm_bo_device *bdev = bo->bdev;
1085718399fSFrançois Tigeot 	struct ttm_tt *ttm = NULL;
1095718399fSFrançois Tigeot 	vm_page_t m, m1, oldm;
1105718399fSFrançois Tigeot 	int ret;
1115718399fSFrançois Tigeot 	int retval = VM_PAGER_OK;
1125718399fSFrançois Tigeot 	struct ttm_mem_type_manager *man =
1135718399fSFrançois Tigeot 		&bdev->man[bo->mem.mem_type];
1145718399fSFrançois Tigeot 
1155718399fSFrançois Tigeot 	vm_object_pip_add(vm_obj, 1);
1165718399fSFrançois Tigeot 	oldm = *mres;
1175718399fSFrançois Tigeot 	if (oldm != NULL) {
1185718399fSFrançois Tigeot 		vm_page_remove(oldm);
1195718399fSFrançois Tigeot 		*mres = NULL;
1205718399fSFrançois Tigeot 	} else
1215718399fSFrançois Tigeot 		oldm = NULL;
1225718399fSFrançois Tigeot retry:
123*04b45e6fSzrj 	VM_OBJECT_UNLOCK(vm_obj);
1245718399fSFrançois Tigeot 	m = NULL;
1255718399fSFrançois Tigeot 
1265718399fSFrançois Tigeot reserve:
1275718399fSFrançois Tigeot 	ret = ttm_bo_reserve(bo, false, false, false, 0);
1285718399fSFrançois Tigeot 	if (unlikely(ret != 0)) {
1295718399fSFrançois Tigeot 		if (ret == -EBUSY) {
1305718399fSFrançois Tigeot 			lwkt_yield();
1315718399fSFrançois Tigeot 			goto reserve;
1325718399fSFrançois Tigeot 		}
1335718399fSFrançois Tigeot 	}
1345718399fSFrançois Tigeot 
1355718399fSFrançois Tigeot 	if (bdev->driver->fault_reserve_notify) {
1365718399fSFrançois Tigeot 		ret = bdev->driver->fault_reserve_notify(bo);
1375718399fSFrançois Tigeot 		switch (ret) {
1385718399fSFrançois Tigeot 		case 0:
1395718399fSFrançois Tigeot 			break;
1405718399fSFrançois Tigeot 		case -EBUSY:
141797013cfSFrançois Tigeot 		case -ERESTARTSYS:
1425718399fSFrançois Tigeot 		case -EINTR:
1435718399fSFrançois Tigeot 			lwkt_yield();
1445718399fSFrançois Tigeot 			goto reserve;
1455718399fSFrançois Tigeot 		default:
1465718399fSFrançois Tigeot 			retval = VM_PAGER_ERROR;
1475718399fSFrançois Tigeot 			goto out_unlock;
1485718399fSFrançois Tigeot 		}
1495718399fSFrançois Tigeot 	}
1505718399fSFrançois Tigeot 
1515718399fSFrançois Tigeot 	/*
1525718399fSFrançois Tigeot 	 * Wait for buffer data in transit, due to a pipelined
1535718399fSFrançois Tigeot 	 * move.
1545718399fSFrançois Tigeot 	 */
1555718399fSFrançois Tigeot 
1565718399fSFrançois Tigeot 	lockmgr(&bdev->fence_lock, LK_EXCLUSIVE);
1575718399fSFrançois Tigeot 	if (test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)) {
1586f486c69SFrançois Tigeot 		/*
1596f486c69SFrançois Tigeot 		 * Here, the behavior differs between Linux and FreeBSD.
1606f486c69SFrançois Tigeot 		 *
1616f486c69SFrançois Tigeot 		 * On Linux, the wait is interruptible (3rd argument to
1626f486c69SFrançois Tigeot 		 * ttm_bo_wait). There must be some mechanism to resume
1636f486c69SFrançois Tigeot 		 * page fault handling, once the signal is processed.
1646f486c69SFrançois Tigeot 		 *
1656f486c69SFrançois Tigeot 		 * On FreeBSD, the wait is uninteruptible. This is not a
1666f486c69SFrançois Tigeot 		 * problem as we can't end up with an unkillable process
1676f486c69SFrançois Tigeot 		 * here, because the wait will eventually time out.
1686f486c69SFrançois Tigeot 		 *
1696f486c69SFrançois Tigeot 		 * An example of this situation is the Xorg process
1706f486c69SFrançois Tigeot 		 * which uses SIGALRM internally. The signal could
1716f486c69SFrançois Tigeot 		 * interrupt the wait, causing the page fault to fail
1726f486c69SFrançois Tigeot 		 * and the process to receive SIGSEGV.
1736f486c69SFrançois Tigeot 		 */
1746f486c69SFrançois Tigeot 		ret = ttm_bo_wait(bo, false, false, false);
1755718399fSFrançois Tigeot 		lockmgr(&bdev->fence_lock, LK_RELEASE);
1765718399fSFrançois Tigeot 		if (unlikely(ret != 0)) {
1775718399fSFrançois Tigeot 			retval = VM_PAGER_ERROR;
1785718399fSFrançois Tigeot 			goto out_unlock;
1795718399fSFrançois Tigeot 		}
1805718399fSFrançois Tigeot 	} else
1815718399fSFrançois Tigeot 		lockmgr(&bdev->fence_lock, LK_RELEASE);
1825718399fSFrançois Tigeot 
1835718399fSFrançois Tigeot 	ret = ttm_mem_io_lock(man, true);
1845718399fSFrançois Tigeot 	if (unlikely(ret != 0)) {
1855718399fSFrançois Tigeot 		retval = VM_PAGER_ERROR;
1865718399fSFrançois Tigeot 		goto out_unlock;
1875718399fSFrançois Tigeot 	}
1885718399fSFrançois Tigeot 	ret = ttm_mem_io_reserve_vm(bo);
1895718399fSFrançois Tigeot 	if (unlikely(ret != 0)) {
1905718399fSFrançois Tigeot 		retval = VM_PAGER_ERROR;
1915718399fSFrançois Tigeot 		goto out_io_unlock;
1925718399fSFrançois Tigeot 	}
1935718399fSFrançois Tigeot 
1945718399fSFrançois Tigeot 	/*
1955718399fSFrançois Tigeot 	 * Strictly, we're not allowed to modify vma->vm_page_prot here,
1965718399fSFrançois Tigeot 	 * since the mmap_sem is only held in read mode. However, we
1975718399fSFrançois Tigeot 	 * modify only the caching bits of vma->vm_page_prot and
1985718399fSFrançois Tigeot 	 * consider those bits protected by
1995718399fSFrançois Tigeot 	 * the bo->mutex, as we should be the only writers.
2005718399fSFrançois Tigeot 	 * There shouldn't really be any readers of these bits except
2015718399fSFrançois Tigeot 	 * within vm_insert_mixed()? fork?
2025718399fSFrançois Tigeot 	 *
2035718399fSFrançois Tigeot 	 * TODO: Add a list of vmas to the bo, and change the
2045718399fSFrançois Tigeot 	 * vma->vm_page_prot when the object changes caching policy, with
2055718399fSFrançois Tigeot 	 * the correct locks held.
2065718399fSFrançois Tigeot 	 */
2075718399fSFrançois Tigeot 	if (!bo->mem.bus.is_iomem) {
2085718399fSFrançois Tigeot 		/* Allocate all page at once, most common usage */
2095718399fSFrançois Tigeot 		ttm = bo->ttm;
2105718399fSFrançois Tigeot 		if (ttm->bdev->driver->ttm_tt_populate(ttm)) {
2115718399fSFrançois Tigeot 			retval = VM_PAGER_ERROR;
2125718399fSFrançois Tigeot 			goto out_io_unlock;
2135718399fSFrançois Tigeot 		}
2145718399fSFrançois Tigeot 	}
2155718399fSFrançois Tigeot 
2165718399fSFrançois Tigeot 	if (bo->mem.bus.is_iomem) {
2175718399fSFrançois Tigeot 		m = vm_phys_fictitious_to_vm_page(bo->mem.bus.base +
2185718399fSFrançois Tigeot 		    bo->mem.bus.offset + offset);
2195718399fSFrançois Tigeot 		pmap_page_set_memattr(m, ttm_io_prot(bo->mem.placement));
2205718399fSFrançois Tigeot 	} else {
2215718399fSFrançois Tigeot 		ttm = bo->ttm;
2225718399fSFrançois Tigeot 		m = ttm->pages[OFF_TO_IDX(offset)];
2235718399fSFrançois Tigeot 		if (unlikely(!m)) {
2245718399fSFrançois Tigeot 			retval = VM_PAGER_ERROR;
2255718399fSFrançois Tigeot 			goto out_io_unlock;
2265718399fSFrançois Tigeot 		}
2275718399fSFrançois Tigeot 		pmap_page_set_memattr(m,
2285718399fSFrançois Tigeot 		    (bo->mem.placement & TTM_PL_FLAG_CACHED) ?
2295718399fSFrançois Tigeot 		    VM_MEMATTR_WRITE_BACK : ttm_io_prot(bo->mem.placement));
2305718399fSFrançois Tigeot 	}
2315718399fSFrançois Tigeot 
232*04b45e6fSzrj 	VM_OBJECT_LOCK(vm_obj);
2335718399fSFrançois Tigeot 	if ((m->flags & PG_BUSY) != 0) {
2345718399fSFrançois Tigeot #if 0
2355718399fSFrançois Tigeot 		vm_page_sleep(m, "ttmpbs");
2365718399fSFrançois Tigeot #endif
2375718399fSFrançois Tigeot 		ttm_mem_io_unlock(man);
2385718399fSFrançois Tigeot 		ttm_bo_unreserve(bo);
2395718399fSFrançois Tigeot 		goto retry;
2405718399fSFrançois Tigeot 	}
2415718399fSFrançois Tigeot 	m->valid = VM_PAGE_BITS_ALL;
2425718399fSFrançois Tigeot 	*mres = m;
2435718399fSFrançois Tigeot 	m1 = vm_page_lookup(vm_obj, OFF_TO_IDX(offset));
2445718399fSFrançois Tigeot 	if (m1 == NULL) {
2455718399fSFrançois Tigeot 		vm_page_insert(m, vm_obj, OFF_TO_IDX(offset));
2465718399fSFrançois Tigeot 	} else {
2475718399fSFrançois Tigeot 		KASSERT(m == m1,
2485718399fSFrançois Tigeot 		    ("inconsistent insert bo %p m %p m1 %p offset %jx",
2495718399fSFrançois Tigeot 		    bo, m, m1, (uintmax_t)offset));
2505718399fSFrançois Tigeot 	}
2515718399fSFrançois Tigeot 	vm_page_busy_try(m, FALSE);
2525718399fSFrançois Tigeot 
2535718399fSFrançois Tigeot 	if (oldm != NULL) {
2545718399fSFrançois Tigeot 		vm_page_free(oldm);
2555718399fSFrançois Tigeot 	}
2565718399fSFrançois Tigeot 
2575718399fSFrançois Tigeot out_io_unlock1:
2585718399fSFrançois Tigeot 	ttm_mem_io_unlock(man);
2595718399fSFrançois Tigeot out_unlock1:
2605718399fSFrançois Tigeot 	ttm_bo_unreserve(bo);
2615718399fSFrançois Tigeot 	vm_object_pip_wakeup(vm_obj);
2625718399fSFrançois Tigeot 	return (retval);
2635718399fSFrançois Tigeot 
2645718399fSFrançois Tigeot out_io_unlock:
265*04b45e6fSzrj 	VM_OBJECT_LOCK(vm_obj);
2665718399fSFrançois Tigeot 	goto out_io_unlock1;
2675718399fSFrançois Tigeot 
2685718399fSFrançois Tigeot out_unlock:
269*04b45e6fSzrj 	VM_OBJECT_LOCK(vm_obj);
2705718399fSFrançois Tigeot 	goto out_unlock1;
2715718399fSFrançois Tigeot }
2725718399fSFrançois Tigeot 
2735718399fSFrançois Tigeot static int
2745718399fSFrançois Tigeot ttm_bo_vm_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
2755718399fSFrançois Tigeot     vm_ooffset_t foff, struct ucred *cred, u_short *color)
2765718399fSFrançois Tigeot {
2775718399fSFrançois Tigeot 
2785718399fSFrançois Tigeot 	/*
2796f486c69SFrançois Tigeot 	 * On Linux, a reference to the buffer object is acquired here.
2806f486c69SFrançois Tigeot 	 * The reason is that this function is not called when the
2816f486c69SFrançois Tigeot 	 * mmap() is initialized, but only when a process forks for
2826f486c69SFrançois Tigeot 	 * instance. Therefore on Linux, the reference on the bo is
2836f486c69SFrançois Tigeot 	 * acquired either in ttm_bo_mmap() or ttm_bo_vm_open(). It's
2846f486c69SFrançois Tigeot 	 * then released in ttm_bo_vm_close().
2856f486c69SFrançois Tigeot 	 *
2866f486c69SFrançois Tigeot 	 * Here, this function is called during mmap() intialization.
2876f486c69SFrançois Tigeot 	 * Thus, the reference acquired in ttm_bo_mmap_single() is
2886f486c69SFrançois Tigeot 	 * sufficient.
2895718399fSFrançois Tigeot 	 */
2905718399fSFrançois Tigeot 	*color = 0;
2915718399fSFrançois Tigeot 	return (0);
2925718399fSFrançois Tigeot }
2935718399fSFrançois Tigeot 
2945718399fSFrançois Tigeot static void
2955718399fSFrançois Tigeot ttm_bo_vm_dtor(void *handle)
2965718399fSFrançois Tigeot {
2975718399fSFrançois Tigeot 	struct ttm_buffer_object *bo = handle;
2985718399fSFrançois Tigeot 
2995718399fSFrançois Tigeot 	ttm_bo_unref(&bo);
3005718399fSFrançois Tigeot }
3015718399fSFrançois Tigeot 
3025718399fSFrançois Tigeot static struct cdev_pager_ops ttm_pager_ops = {
3035718399fSFrançois Tigeot 	.cdev_pg_fault = ttm_bo_vm_fault,
3045718399fSFrançois Tigeot 	.cdev_pg_ctor = ttm_bo_vm_ctor,
3055718399fSFrançois Tigeot 	.cdev_pg_dtor = ttm_bo_vm_dtor
3065718399fSFrançois Tigeot };
3075718399fSFrançois Tigeot 
3085718399fSFrançois Tigeot int
3095718399fSFrançois Tigeot ttm_bo_mmap_single(struct ttm_bo_device *bdev, vm_ooffset_t *offset, vm_size_t size,
3105718399fSFrançois Tigeot     struct vm_object **obj_res, int nprot)
3115718399fSFrançois Tigeot {
3125718399fSFrançois Tigeot 	struct ttm_bo_driver *driver;
3135718399fSFrançois Tigeot 	struct ttm_buffer_object *bo;
3145718399fSFrançois Tigeot 	struct vm_object *vm_obj;
3155718399fSFrançois Tigeot 	int ret;
3165718399fSFrançois Tigeot 
317f6201ebfSMatthew Dillon 	*obj_res = NULL;
318f6201ebfSMatthew Dillon 
3195718399fSFrançois Tigeot 	lockmgr(&bdev->vm_lock, LK_EXCLUSIVE);
3205718399fSFrançois Tigeot 	bo = ttm_bo_vm_lookup_rb(bdev, OFF_TO_IDX(*offset), OFF_TO_IDX(size));
3215718399fSFrançois Tigeot 	if (likely(bo != NULL))
322e3b244c9SFrançois Tigeot 		kref_get(&bo->kref);
3235718399fSFrançois Tigeot 	lockmgr(&bdev->vm_lock, LK_RELEASE);
3245718399fSFrançois Tigeot 
3255718399fSFrançois Tigeot 	if (unlikely(bo == NULL)) {
3260bece63dSImre Vadasz 		pr_err("Could not find buffer object to map\n");
3275718399fSFrançois Tigeot 		return (EINVAL);
3285718399fSFrançois Tigeot 	}
3295718399fSFrançois Tigeot 
3305718399fSFrançois Tigeot 	driver = bo->bdev->driver;
3315718399fSFrançois Tigeot 	if (unlikely(!driver->verify_access)) {
3325718399fSFrançois Tigeot 		ret = EPERM;
3335718399fSFrançois Tigeot 		goto out_unref;
3345718399fSFrançois Tigeot 	}
3355718399fSFrançois Tigeot 	ret = -driver->verify_access(bo);
3365718399fSFrançois Tigeot 	if (unlikely(ret != 0))
3375718399fSFrançois Tigeot 		goto out_unref;
3385718399fSFrançois Tigeot 
3395718399fSFrançois Tigeot 	vm_obj = cdev_pager_allocate(bo, OBJT_MGTDEVICE, &ttm_pager_ops,
3405718399fSFrançois Tigeot 	    size, nprot, 0, curthread->td_ucred);
341f6201ebfSMatthew Dillon 
3425718399fSFrançois Tigeot 	if (vm_obj == NULL) {
3435718399fSFrançois Tigeot 		ret = EINVAL;
3445718399fSFrançois Tigeot 		goto out_unref;
3455718399fSFrançois Tigeot 	}
3465718399fSFrançois Tigeot 	/*
3475718399fSFrançois Tigeot 	 * Note: We're transferring the bo reference to vm_obj->handle here.
3485718399fSFrançois Tigeot 	 */
3495718399fSFrançois Tigeot 	*offset = 0;
3505718399fSFrançois Tigeot 	*obj_res = vm_obj;
3515718399fSFrançois Tigeot 	return 0;
3525718399fSFrançois Tigeot out_unref:
3535718399fSFrançois Tigeot 	ttm_bo_unref(&bo);
3545718399fSFrançois Tigeot 	return ret;
3555718399fSFrançois Tigeot }
356c66857ebSFrançois Tigeot EXPORT_SYMBOL(ttm_bo_mmap);
3575718399fSFrançois Tigeot 
3586f486c69SFrançois Tigeot void
3596f486c69SFrançois Tigeot ttm_bo_release_mmap(struct ttm_buffer_object *bo)
3606f486c69SFrançois Tigeot {
3616f486c69SFrançois Tigeot 	vm_object_t vm_obj;
3626f486c69SFrançois Tigeot 	vm_page_t m;
3636f486c69SFrançois Tigeot 	int i;
3646f486c69SFrançois Tigeot 
3656f486c69SFrançois Tigeot 	vm_obj = cdev_pager_lookup(bo);
3666f486c69SFrançois Tigeot 	if (vm_obj == NULL)
3676f486c69SFrançois Tigeot 		return;
3686f486c69SFrançois Tigeot 
369*04b45e6fSzrj 	VM_OBJECT_LOCK(vm_obj);
3706f486c69SFrançois Tigeot 	for (i = 0; i < bo->num_pages; i++) {
3716f486c69SFrançois Tigeot 		m = vm_page_lookup_busy_wait(vm_obj, i, TRUE, "ttm_unm");
3726f486c69SFrançois Tigeot 		if (m == NULL)
3736f486c69SFrançois Tigeot 			continue;
3746f486c69SFrançois Tigeot 		cdev_pager_free_page(vm_obj, m);
3756f486c69SFrançois Tigeot 	}
376*04b45e6fSzrj 	VM_OBJECT_UNLOCK(vm_obj);
3776f486c69SFrançois Tigeot 
3786f486c69SFrançois Tigeot 	vm_object_deallocate(vm_obj);
3796f486c69SFrançois Tigeot }
3806f486c69SFrançois Tigeot 
3815718399fSFrançois Tigeot #if 0
3825718399fSFrançois Tigeot int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo)
3835718399fSFrançois Tigeot {
3845718399fSFrançois Tigeot 	if (vma->vm_pgoff != 0)
3855718399fSFrançois Tigeot 		return -EACCES;
3865718399fSFrançois Tigeot 
3875718399fSFrançois Tigeot 	vma->vm_ops = &ttm_bo_vm_ops;
3885718399fSFrançois Tigeot 	vma->vm_private_data = ttm_bo_reference(bo);
3895718399fSFrançois Tigeot 	vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND;
3905718399fSFrançois Tigeot 	return 0;
3915718399fSFrançois Tigeot }
392c66857ebSFrançois Tigeot EXPORT_SYMBOL(ttm_fbdev_mmap);
3935718399fSFrançois Tigeot #endif
394