1247835Skib/************************************************************************** 2247835Skib * 3247835Skib * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA 4247835Skib * All Rights Reserved. 5247835Skib * 6247835Skib * Permission is hereby granted, free of charge, to any person obtaining a 7247835Skib * copy of this software and associated documentation files (the 8247835Skib * "Software"), to deal in the Software without restriction, including 9247835Skib * without limitation the rights to use, copy, modify, merge, publish, 10247835Skib * distribute, sub license, and/or sell copies of the Software, and to 11247835Skib * permit persons to whom the Software is furnished to do so, subject to 12247835Skib * the following conditions: 13247835Skib * 14247835Skib * The above copyright notice and this permission notice (including the 15247835Skib * next paragraph) shall be included in all copies or substantial portions 16247835Skib * of the Software. 17247835Skib * 18247835Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19247835Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20247835Skib * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21247835Skib * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 22247835Skib * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23247835Skib * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24247835Skib * USE OR OTHER DEALINGS IN THE SOFTWARE. 25247835Skib * 26247835Skib **************************************************************************/ 27247835Skib/* 28247835Skib * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> 29247835Skib */ 30247835Skib/* 31247835Skib * Copyright (c) 2013 The FreeBSD Foundation 32247835Skib * All rights reserved. 33247835Skib * 34247835Skib * Portions of this software were developed by Konstantin Belousov 35247835Skib * <kib@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 36247835Skib */ 37247835Skib 38247835Skib#include <sys/cdefs.h> 39247835Skib__FBSDID("$FreeBSD$"); 40247835Skib 41247835Skib#include "opt_vm.h" 42247835Skib 43247835Skib#include <dev/drm2/drmP.h> 44247835Skib#include <dev/drm2/ttm/ttm_module.h> 45247835Skib#include <dev/drm2/ttm/ttm_bo_driver.h> 46247835Skib#include <dev/drm2/ttm/ttm_placement.h> 47247835Skib 48247835Skib#include <vm/vm.h> 49247835Skib#include <vm/vm_page.h> 50247835Skib 51247835Skib#define TTM_BO_VM_NUM_PREFAULT 16 52247835Skib 53247835SkibRB_GENERATE(ttm_bo_device_buffer_objects, ttm_buffer_object, vm_rb, 54247835Skib ttm_bo_cmp_rb_tree_items); 55247835Skib 56247835Skibint 57247835Skibttm_bo_cmp_rb_tree_items(struct ttm_buffer_object *a, 58247835Skib struct ttm_buffer_object *b) 59247835Skib{ 60247835Skib 61247835Skib if (a->vm_node->start < b->vm_node->start) { 62247835Skib return (-1); 63247835Skib } else if (a->vm_node->start > b->vm_node->start) { 64247835Skib return (1); 65247835Skib } else { 66247835Skib return (0); 67247835Skib } 68247835Skib} 69247835Skib 70247835Skibstatic struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev, 71247835Skib unsigned long page_start, 72247835Skib unsigned long num_pages) 73247835Skib{ 74247835Skib unsigned long cur_offset; 75247835Skib struct ttm_buffer_object *bo; 76247835Skib struct ttm_buffer_object *best_bo = NULL; 77247835Skib 78262988Sdumbbell bo = RB_ROOT(&bdev->addr_space_rb); 79262988Sdumbbell while (bo != NULL) { 80247835Skib cur_offset = bo->vm_node->start; 81247835Skib if (page_start >= cur_offset) { 82247835Skib best_bo = bo; 83247835Skib if (page_start == cur_offset) 84247835Skib break; 85262988Sdumbbell bo = RB_RIGHT(bo, vm_rb); 86262988Sdumbbell } else 87262988Sdumbbell bo = RB_LEFT(bo, vm_rb); 88247835Skib } 89247835Skib 90247835Skib if (unlikely(best_bo == NULL)) 91247835Skib return NULL; 92247835Skib 93247835Skib if (unlikely((best_bo->vm_node->start + best_bo->num_pages) < 94247835Skib (page_start + num_pages))) 95247835Skib return NULL; 96247835Skib 97247835Skib return best_bo; 98247835Skib} 99247835Skib 100247835Skibstatic int 101247835Skibttm_bo_vm_fault(vm_object_t vm_obj, vm_ooffset_t offset, 102247835Skib int prot, vm_page_t *mres) 103247835Skib{ 104247835Skib 105247835Skib struct ttm_buffer_object *bo = vm_obj->handle; 106247835Skib struct ttm_bo_device *bdev = bo->bdev; 107247835Skib struct ttm_tt *ttm = NULL; 108262988Sdumbbell vm_page_t m, m1, oldm; 109247835Skib int ret; 110247835Skib int retval = VM_PAGER_OK; 111247835Skib struct ttm_mem_type_manager *man = 112247835Skib &bdev->man[bo->mem.mem_type]; 113247835Skib 114247835Skib vm_object_pip_add(vm_obj, 1); 115247835Skib oldm = *mres; 116247835Skib if (oldm != NULL) { 117247835Skib vm_page_lock(oldm); 118247835Skib vm_page_remove(oldm); 119247835Skib vm_page_unlock(oldm); 120247835Skib *mres = NULL; 121247835Skib } else 122247835Skib oldm = NULL; 123247835Skibretry: 124247835Skib VM_OBJECT_UNLOCK(vm_obj); 125247835Skib m = NULL; 126247835Skib 127247835Skibreserve: 128262988Sdumbbell ret = ttm_bo_reserve(bo, false, false, false, 0); 129247835Skib if (unlikely(ret != 0)) { 130247835Skib if (ret == -EBUSY) { 131247835Skib kern_yield(0); 132247835Skib goto reserve; 133247835Skib } 134247835Skib } 135247835Skib 136247835Skib if (bdev->driver->fault_reserve_notify) { 137247835Skib ret = bdev->driver->fault_reserve_notify(bo); 138247835Skib switch (ret) { 139247835Skib case 0: 140247835Skib break; 141247835Skib case -EBUSY: 142247835Skib case -ERESTART: 143247835Skib case -EINTR: 144247835Skib kern_yield(0); 145247835Skib goto reserve; 146247835Skib default: 147247835Skib retval = VM_PAGER_ERROR; 148247835Skib goto out_unlock; 149247835Skib } 150247835Skib } 151247835Skib 152247835Skib /* 153247835Skib * Wait for buffer data in transit, due to a pipelined 154247835Skib * move. 155247835Skib */ 156247835Skib 157247835Skib mtx_lock(&bdev->fence_lock); 158263119Sdumbbell if (test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)) { 159262988Sdumbbell /* 160262988Sdumbbell * Here, the behavior differs between Linux and FreeBSD. 161262988Sdumbbell * 162262988Sdumbbell * On Linux, the wait is interruptible (3rd argument to 163262988Sdumbbell * ttm_bo_wait). There must be some mechanism to resume 164262988Sdumbbell * page fault handling, once the signal is processed. 165262988Sdumbbell * 166262988Sdumbbell * On FreeBSD, the wait is uninteruptible. This is not a 167262988Sdumbbell * problem as we can't end up with an unkillable process 168262988Sdumbbell * here, because the wait will eventually time out. 169262988Sdumbbell * 170262988Sdumbbell * An example of this situation is the Xorg process 171262988Sdumbbell * which uses SIGALRM internally. The signal could 172262988Sdumbbell * interrupt the wait, causing the page fault to fail 173262988Sdumbbell * and the process to receive SIGSEGV. 174262988Sdumbbell */ 175262988Sdumbbell ret = ttm_bo_wait(bo, false, false, false); 176247835Skib mtx_unlock(&bdev->fence_lock); 177247835Skib if (unlikely(ret != 0)) { 178247835Skib retval = VM_PAGER_ERROR; 179247835Skib goto out_unlock; 180247835Skib } 181247835Skib } else 182247835Skib mtx_unlock(&bdev->fence_lock); 183247835Skib 184247835Skib ret = ttm_mem_io_lock(man, true); 185247835Skib if (unlikely(ret != 0)) { 186247835Skib retval = VM_PAGER_ERROR; 187247835Skib goto out_unlock; 188247835Skib } 189247835Skib ret = ttm_mem_io_reserve_vm(bo); 190247835Skib if (unlikely(ret != 0)) { 191247835Skib retval = VM_PAGER_ERROR; 192247835Skib goto out_io_unlock; 193247835Skib } 194247835Skib 195247835Skib /* 196247835Skib * Strictly, we're not allowed to modify vma->vm_page_prot here, 197247835Skib * since the mmap_sem is only held in read mode. However, we 198247835Skib * modify only the caching bits of vma->vm_page_prot and 199247835Skib * consider those bits protected by 200247835Skib * the bo->mutex, as we should be the only writers. 201247835Skib * There shouldn't really be any readers of these bits except 202247835Skib * within vm_insert_mixed()? fork? 203247835Skib * 204247835Skib * TODO: Add a list of vmas to the bo, and change the 205247835Skib * vma->vm_page_prot when the object changes caching policy, with 206247835Skib * the correct locks held. 207247835Skib */ 208247835Skib if (!bo->mem.bus.is_iomem) { 209247835Skib /* Allocate all page at once, most common usage */ 210247835Skib ttm = bo->ttm; 211247835Skib if (ttm->bdev->driver->ttm_tt_populate(ttm)) { 212247835Skib retval = VM_PAGER_ERROR; 213247835Skib goto out_io_unlock; 214247835Skib } 215247835Skib } 216247835Skib 217247835Skib if (bo->mem.bus.is_iomem) { 218247835Skib m = vm_phys_fictitious_to_vm_page(bo->mem.bus.base + 219247835Skib bo->mem.bus.offset + offset); 220247835Skib pmap_page_set_memattr(m, ttm_io_prot(bo->mem.placement)); 221247835Skib } else { 222247835Skib ttm = bo->ttm; 223247835Skib m = ttm->pages[OFF_TO_IDX(offset)]; 224247835Skib if (unlikely(!m)) { 225247835Skib retval = VM_PAGER_ERROR; 226247835Skib goto out_io_unlock; 227247835Skib } 228247835Skib pmap_page_set_memattr(m, 229247835Skib (bo->mem.placement & TTM_PL_FLAG_CACHED) ? 230247835Skib VM_MEMATTR_WRITE_BACK : ttm_io_prot(bo->mem.placement)); 231247835Skib } 232247835Skib 233247835Skib VM_OBJECT_LOCK(vm_obj); 234247835Skib if ((m->flags & VPO_BUSY) != 0) { 235247835Skib vm_page_sleep(m, "ttmpbs"); 236247835Skib ttm_mem_io_unlock(man); 237247835Skib ttm_bo_unreserve(bo); 238247835Skib goto retry; 239247835Skib } 240247835Skib m->valid = VM_PAGE_BITS_ALL; 241247835Skib *mres = m; 242262988Sdumbbell m1 = vm_page_lookup(vm_obj, OFF_TO_IDX(offset)); 243262988Sdumbbell if (m1 == NULL) { 244262988Sdumbbell vm_page_insert(m, vm_obj, OFF_TO_IDX(offset)); 245262988Sdumbbell } else { 246262988Sdumbbell KASSERT(m == m1, 247262988Sdumbbell ("inconsistent insert bo %p m %p m1 %p offset %jx", 248262988Sdumbbell bo, m, m1, (uintmax_t)offset)); 249262988Sdumbbell } 250247835Skib vm_page_busy(m); 251247835Skib 252247835Skib if (oldm != NULL) { 253247835Skib vm_page_lock(oldm); 254247835Skib vm_page_free(oldm); 255247835Skib vm_page_unlock(oldm); 256247835Skib } 257247835Skib 258247835Skibout_io_unlock1: 259247835Skib ttm_mem_io_unlock(man); 260247835Skibout_unlock1: 261247835Skib ttm_bo_unreserve(bo); 262247835Skib vm_object_pip_wakeup(vm_obj); 263247835Skib return (retval); 264247835Skib 265247835Skibout_io_unlock: 266247835Skib VM_OBJECT_LOCK(vm_obj); 267247835Skib goto out_io_unlock1; 268247835Skib 269247835Skibout_unlock: 270247835Skib VM_OBJECT_LOCK(vm_obj); 271247835Skib goto out_unlock1; 272247835Skib} 273247835Skib 274247835Skibstatic int 275247835Skibttm_bo_vm_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, 276247835Skib vm_ooffset_t foff, struct ucred *cred, u_short *color) 277247835Skib{ 278247835Skib 279262988Sdumbbell /* 280262988Sdumbbell * On Linux, a reference to the buffer object is acquired here. 281262988Sdumbbell * The reason is that this function is not called when the 282262988Sdumbbell * mmap() is initialized, but only when a process forks for 283262988Sdumbbell * instance. Therefore on Linux, the reference on the bo is 284262988Sdumbbell * acquired either in ttm_bo_mmap() or ttm_bo_vm_open(). It's 285262988Sdumbbell * then released in ttm_bo_vm_close(). 286262988Sdumbbell * 287262988Sdumbbell * Here, this function is called during mmap() intialization. 288262988Sdumbbell * Thus, the reference acquired in ttm_bo_mmap_single() is 289262988Sdumbbell * sufficient. 290262988Sdumbbell */ 291262988Sdumbbell 292247835Skib *color = 0; 293247835Skib return (0); 294247835Skib} 295247835Skib 296247835Skibstatic void 297247835Skibttm_bo_vm_dtor(void *handle) 298247835Skib{ 299247835Skib struct ttm_buffer_object *bo = handle; 300247835Skib 301247835Skib ttm_bo_unref(&bo); 302247835Skib} 303247835Skib 304247835Skibstatic struct cdev_pager_ops ttm_pager_ops = { 305247835Skib .cdev_pg_fault = ttm_bo_vm_fault, 306247835Skib .cdev_pg_ctor = ttm_bo_vm_ctor, 307247835Skib .cdev_pg_dtor = ttm_bo_vm_dtor 308247835Skib}; 309247835Skib 310247835Skibint 311247835Skibttm_bo_mmap_single(struct ttm_bo_device *bdev, vm_ooffset_t *offset, vm_size_t size, 312247835Skib struct vm_object **obj_res, int nprot) 313247835Skib{ 314247835Skib struct ttm_bo_driver *driver; 315247835Skib struct ttm_buffer_object *bo; 316247835Skib struct vm_object *vm_obj; 317247835Skib int ret; 318247835Skib 319247835Skib rw_wlock(&bdev->vm_lock); 320247835Skib bo = ttm_bo_vm_lookup_rb(bdev, OFF_TO_IDX(*offset), OFF_TO_IDX(size)); 321247835Skib if (likely(bo != NULL)) 322247835Skib refcount_acquire(&bo->kref); 323247835Skib rw_wunlock(&bdev->vm_lock); 324247835Skib 325247835Skib if (unlikely(bo == NULL)) { 326247835Skib printf("[TTM] Could not find buffer object to map\n"); 327247835Skib return (EINVAL); 328247835Skib } 329247835Skib 330247835Skib driver = bo->bdev->driver; 331247835Skib if (unlikely(!driver->verify_access)) { 332247835Skib ret = EPERM; 333247835Skib goto out_unref; 334247835Skib } 335247835Skib ret = -driver->verify_access(bo); 336247835Skib if (unlikely(ret != 0)) 337247835Skib goto out_unref; 338247835Skib 339247835Skib vm_obj = cdev_pager_allocate(bo, OBJT_MGTDEVICE, &ttm_pager_ops, 340247835Skib size, nprot, 0, curthread->td_ucred); 341247835Skib if (vm_obj == NULL) { 342247835Skib ret = EINVAL; 343247835Skib goto out_unref; 344247835Skib } 345247835Skib /* 346247835Skib * Note: We're transferring the bo reference to vm_obj->handle here. 347247835Skib */ 348247835Skib *offset = 0; 349247835Skib *obj_res = vm_obj; 350247835Skib return 0; 351247835Skibout_unref: 352247835Skib ttm_bo_unref(&bo); 353247835Skib return ret; 354247835Skib} 355247835Skib 356262988Sdumbbellvoid 357262988Sdumbbellttm_bo_release_mmap(struct ttm_buffer_object *bo) 358262988Sdumbbell{ 359262988Sdumbbell vm_object_t vm_obj; 360262988Sdumbbell vm_page_t m; 361262988Sdumbbell int i; 362262988Sdumbbell 363262988Sdumbbell vm_obj = cdev_pager_lookup(bo); 364262988Sdumbbell if (vm_obj == NULL) 365262988Sdumbbell return; 366262988Sdumbbell 367262988Sdumbbell VM_OBJECT_LOCK(vm_obj); 368262988Sdumbbellretry: 369262988Sdumbbell for (i = 0; i < bo->num_pages; i++) { 370262988Sdumbbell m = vm_page_lookup(vm_obj, i); 371262988Sdumbbell if (m == NULL) 372262988Sdumbbell continue; 373262988Sdumbbell if (vm_page_sleep_if_busy(m, true, "ttm_unm")) 374262988Sdumbbell goto retry; 375262988Sdumbbell cdev_pager_free_page(vm_obj, m); 376262988Sdumbbell } 377262988Sdumbbell VM_OBJECT_UNLOCK(vm_obj); 378262988Sdumbbell 379262988Sdumbbell vm_object_deallocate(vm_obj); 380262988Sdumbbell} 381262988Sdumbbell 382247835Skib#if 0 383247835Skibint ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo) 384247835Skib{ 385247835Skib if (vma->vm_pgoff != 0) 386247835Skib return -EACCES; 387247835Skib 388247835Skib vma->vm_ops = &ttm_bo_vm_ops; 389247835Skib vma->vm_private_data = ttm_bo_reference(bo); 390247835Skib vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND; 391247835Skib return 0; 392247835Skib} 393247835Skib 394247835Skibssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp, 395247835Skib const char __user *wbuf, char __user *rbuf, size_t count, 396247835Skib loff_t *f_pos, bool write) 397247835Skib{ 398247835Skib struct ttm_buffer_object *bo; 399247835Skib struct ttm_bo_driver *driver; 400247835Skib struct ttm_bo_kmap_obj map; 401247835Skib unsigned long dev_offset = (*f_pos >> PAGE_SHIFT); 402247835Skib unsigned long kmap_offset; 403247835Skib unsigned long kmap_end; 404247835Skib unsigned long kmap_num; 405247835Skib size_t io_size; 406247835Skib unsigned int page_offset; 407247835Skib char *virtual; 408247835Skib int ret; 409247835Skib bool no_wait = false; 410247835Skib bool dummy; 411247835Skib 412247835Skib read_lock(&bdev->vm_lock); 413247835Skib bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1); 414247835Skib if (likely(bo != NULL)) 415247835Skib ttm_bo_reference(bo); 416247835Skib read_unlock(&bdev->vm_lock); 417247835Skib 418247835Skib if (unlikely(bo == NULL)) 419247835Skib return -EFAULT; 420247835Skib 421247835Skib driver = bo->bdev->driver; 422247835Skib if (unlikely(!driver->verify_access)) { 423247835Skib ret = -EPERM; 424247835Skib goto out_unref; 425247835Skib } 426247835Skib 427247835Skib ret = driver->verify_access(bo, filp); 428247835Skib if (unlikely(ret != 0)) 429247835Skib goto out_unref; 430247835Skib 431247835Skib kmap_offset = dev_offset - bo->vm_node->start; 432247835Skib if (unlikely(kmap_offset >= bo->num_pages)) { 433247835Skib ret = -EFBIG; 434247835Skib goto out_unref; 435247835Skib } 436247835Skib 437247835Skib page_offset = *f_pos & ~PAGE_MASK; 438247835Skib io_size = bo->num_pages - kmap_offset; 439247835Skib io_size = (io_size << PAGE_SHIFT) - page_offset; 440247835Skib if (count < io_size) 441247835Skib io_size = count; 442247835Skib 443247835Skib kmap_end = (*f_pos + count - 1) >> PAGE_SHIFT; 444247835Skib kmap_num = kmap_end - kmap_offset + 1; 445247835Skib 446247835Skib ret = ttm_bo_reserve(bo, true, no_wait, false, 0); 447247835Skib 448247835Skib switch (ret) { 449247835Skib case 0: 450247835Skib break; 451247835Skib case -EBUSY: 452247835Skib ret = -EAGAIN; 453247835Skib goto out_unref; 454247835Skib default: 455247835Skib goto out_unref; 456247835Skib } 457247835Skib 458247835Skib ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map); 459247835Skib if (unlikely(ret != 0)) { 460247835Skib ttm_bo_unreserve(bo); 461247835Skib goto out_unref; 462247835Skib } 463247835Skib 464247835Skib virtual = ttm_kmap_obj_virtual(&map, &dummy); 465247835Skib virtual += page_offset; 466247835Skib 467247835Skib if (write) 468247835Skib ret = copy_from_user(virtual, wbuf, io_size); 469247835Skib else 470247835Skib ret = copy_to_user(rbuf, virtual, io_size); 471247835Skib 472247835Skib ttm_bo_kunmap(&map); 473247835Skib ttm_bo_unreserve(bo); 474247835Skib ttm_bo_unref(&bo); 475247835Skib 476247835Skib if (unlikely(ret != 0)) 477247835Skib return -EFBIG; 478247835Skib 479247835Skib *f_pos += io_size; 480247835Skib 481247835Skib return io_size; 482247835Skibout_unref: 483247835Skib ttm_bo_unref(&bo); 484247835Skib return ret; 485247835Skib} 486247835Skib 487247835Skibssize_t ttm_bo_fbdev_io(struct ttm_buffer_object *bo, const char __user *wbuf, 488247835Skib char __user *rbuf, size_t count, loff_t *f_pos, 489247835Skib bool write) 490247835Skib{ 491247835Skib struct ttm_bo_kmap_obj map; 492247835Skib unsigned long kmap_offset; 493247835Skib unsigned long kmap_end; 494247835Skib unsigned long kmap_num; 495247835Skib size_t io_size; 496247835Skib unsigned int page_offset; 497247835Skib char *virtual; 498247835Skib int ret; 499247835Skib bool no_wait = false; 500247835Skib bool dummy; 501247835Skib 502247835Skib kmap_offset = (*f_pos >> PAGE_SHIFT); 503247835Skib if (unlikely(kmap_offset >= bo->num_pages)) 504247835Skib return -EFBIG; 505247835Skib 506247835Skib page_offset = *f_pos & ~PAGE_MASK; 507247835Skib io_size = bo->num_pages - kmap_offset; 508247835Skib io_size = (io_size << PAGE_SHIFT) - page_offset; 509247835Skib if (count < io_size) 510247835Skib io_size = count; 511247835Skib 512247835Skib kmap_end = (*f_pos + count - 1) >> PAGE_SHIFT; 513247835Skib kmap_num = kmap_end - kmap_offset + 1; 514247835Skib 515247835Skib ret = ttm_bo_reserve(bo, true, no_wait, false, 0); 516247835Skib 517247835Skib switch (ret) { 518247835Skib case 0: 519247835Skib break; 520247835Skib case -EBUSY: 521247835Skib return -EAGAIN; 522247835Skib default: 523247835Skib return ret; 524247835Skib } 525247835Skib 526247835Skib ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map); 527247835Skib if (unlikely(ret != 0)) { 528247835Skib ttm_bo_unreserve(bo); 529247835Skib return ret; 530247835Skib } 531247835Skib 532247835Skib virtual = ttm_kmap_obj_virtual(&map, &dummy); 533247835Skib virtual += page_offset; 534247835Skib 535247835Skib if (write) 536247835Skib ret = copy_from_user(virtual, wbuf, io_size); 537247835Skib else 538247835Skib ret = copy_to_user(rbuf, virtual, io_size); 539247835Skib 540247835Skib ttm_bo_kunmap(&map); 541247835Skib ttm_bo_unreserve(bo); 542247835Skib ttm_bo_unref(&bo); 543247835Skib 544247835Skib if (unlikely(ret != 0)) 545247835Skib return ret; 546247835Skib 547247835Skib *f_pos += io_size; 548247835Skib 549247835Skib return io_size; 550247835Skib} 551247835Skib#endif 552