linux/drivers/gpu/drm/ttm/ttm_bo_vm.c
<<
>>
Prefs
   1/**************************************************************************
   2 *
   3 * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
   4 * All Rights Reserved.
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a
   7 * copy of this software and associated documentation files (the
   8 * "Software"), to deal in the Software without restriction, including
   9 * without limitation the rights to use, copy, modify, merge, publish,
  10 * distribute, sub license, and/or sell copies of the Software, and to
  11 * permit persons to whom the Software is furnished to do so, subject to
  12 * the following conditions:
  13 *
  14 * The above copyright notice and this permission notice (including the
  15 * next paragraph) shall be included in all copies or substantial portions
  16 * of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  21 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
  25 *
  26 **************************************************************************/
  27/*
  28 * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
  29 */
  30
  31#define pr_fmt(fmt) "[TTM] " fmt
  32
  33#include <ttm/ttm_module.h>
  34#include <ttm/ttm_bo_driver.h>
  35#include <ttm/ttm_placement.h>
  36#include <drm/drm_vma_manager.h>
  37#include <linux/mm.h>
  38#include <linux/rbtree.h>
  39#include <linux/module.h>
  40#include <linux/uaccess.h>
  41
  42#define TTM_BO_VM_NUM_PREFAULT 16
  43
  44static int ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo,
  45                                struct vm_area_struct *vma,
  46                                struct vm_fault *vmf)
  47{
  48        int ret = 0;
  49
  50        if (likely(!test_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags)))
  51                goto out_unlock;
  52
  53        /*
  54         * Quick non-stalling check for idle.
  55         */
  56        ret = ttm_bo_wait(bo, false, false, true);
  57        if (likely(ret == 0))
  58                goto out_unlock;
  59
  60        /*
  61         * If possible, avoid waiting for GPU with mmap_sem
  62         * held.
  63         */
  64        if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) {
  65                ret = VM_FAULT_RETRY;
  66                if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT)
  67                        goto out_unlock;
  68
  69                up_read(&vma->vm_mm->mmap_sem);
  70                (void) ttm_bo_wait(bo, false, true, false);
  71                goto out_unlock;
  72        }
  73
  74        /*
  75         * Ordinary wait.
  76         */
  77        ret = ttm_bo_wait(bo, false, true, false);
  78        if (unlikely(ret != 0))
  79                ret = (ret != -ERESTARTSYS) ? VM_FAULT_SIGBUS :
  80                        VM_FAULT_NOPAGE;
  81
  82out_unlock:
  83        return ret;
  84}
  85
  86static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
  87{
  88        struct ttm_buffer_object *bo = (struct ttm_buffer_object *)
  89            vma->vm_private_data;
  90        struct ttm_bo_device *bdev = bo->bdev;
  91        unsigned long page_offset;
  92        unsigned long page_last;
  93        unsigned long pfn;
  94        struct ttm_tt *ttm = NULL;
  95        struct page *page;
  96        int ret;
  97        int i;
  98        unsigned long address = (unsigned long)vmf->virtual_address;
  99        int retval = VM_FAULT_NOPAGE;
 100        struct ttm_mem_type_manager *man =
 101                &bdev->man[bo->mem.mem_type];
 102        struct vm_area_struct cvma;
 103
 104        /*
 105         * Work around locking order reversal in fault / nopfn
 106         * between mmap_sem and bo_reserve: Perform a trylock operation
 107         * for reserve, and if it fails, retry the fault after waiting
 108         * for the buffer to become unreserved.
 109         */
 110        ret = ttm_bo_reserve(bo, true, true, false, NULL);
 111        if (unlikely(ret != 0)) {
 112                if (ret != -EBUSY)
 113                        return VM_FAULT_NOPAGE;
 114
 115                if (vmf->flags & FAULT_FLAG_ALLOW_RETRY) {
 116                        if (!(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) {
 117                                up_read(&vma->vm_mm->mmap_sem);
 118                                (void) ttm_bo_wait_unreserved(bo);
 119                        }
 120
 121                        return VM_FAULT_RETRY;
 122                }
 123
 124                /*
 125                 * If we'd want to change locking order to
 126                 * mmap_sem -> bo::reserve, we'd use a blocking reserve here
 127                 * instead of retrying the fault...
 128                 */
 129                return VM_FAULT_NOPAGE;
 130        }
 131
 132        /*
 133         * Refuse to fault imported pages. This should be handled
 134         * (if at all) by redirecting mmap to the exporter.
 135         */
 136        if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) {
 137                retval = VM_FAULT_SIGBUS;
 138                goto out_unlock;
 139        }
 140
 141        if (bdev->driver->fault_reserve_notify) {
 142                ret = bdev->driver->fault_reserve_notify(bo);
 143                switch (ret) {
 144                case 0:
 145                        break;
 146                case -EBUSY:
 147                case -ERESTARTSYS:
 148                        retval = VM_FAULT_NOPAGE;
 149                        goto out_unlock;
 150                default:
 151                        retval = VM_FAULT_SIGBUS;
 152                        goto out_unlock;
 153                }
 154        }
 155
 156        /*
 157         * Wait for buffer data in transit, due to a pipelined
 158         * move.
 159         */
 160        ret = ttm_bo_vm_fault_idle(bo, vma, vmf);
 161        if (unlikely(ret != 0)) {
 162                retval = ret;
 163                goto out_unlock;
 164        }
 165
 166        ret = ttm_mem_io_lock(man, true);
 167        if (unlikely(ret != 0)) {
 168                retval = VM_FAULT_NOPAGE;
 169                goto out_unlock;
 170        }
 171        ret = ttm_mem_io_reserve_vm(bo);
 172        if (unlikely(ret != 0)) {
 173                retval = VM_FAULT_SIGBUS;
 174                goto out_io_unlock;
 175        }
 176
 177        page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
 178                vma->vm_pgoff - drm_vma_node_start(&bo->vma_node);
 179        page_last = vma_pages(vma) + vma->vm_pgoff -
 180                drm_vma_node_start(&bo->vma_node);
 181
 182        if (unlikely(page_offset >= bo->num_pages)) {
 183                retval = VM_FAULT_SIGBUS;
 184                goto out_io_unlock;
 185        }
 186
 187        /*
 188         * Make a local vma copy to modify the page_prot member
 189         * and vm_flags if necessary. The vma parameter is protected
 190         * by mmap_sem in write mode.
 191         */
 192        cvma = *vma;
 193        cvma.vm_page_prot = vm_get_page_prot(cvma.vm_flags);
 194
 195        if (bo->mem.bus.is_iomem) {
 196                cvma.vm_page_prot = ttm_io_prot(bo->mem.placement,
 197                                                cvma.vm_page_prot);
 198        } else {
 199                ttm = bo->ttm;
 200                cvma.vm_page_prot = ttm_io_prot(bo->mem.placement,
 201                                                cvma.vm_page_prot);
 202
 203                /* Allocate all page at once, most common usage */
 204                if (ttm->bdev->driver->ttm_tt_populate(ttm)) {
 205                        retval = VM_FAULT_OOM;
 206                        goto out_io_unlock;
 207                }
 208        }
 209
 210        /*
 211         * Speculatively prefault a number of pages. Only error on
 212         * first page.
 213         */
 214        for (i = 0; i < TTM_BO_VM_NUM_PREFAULT; ++i) {
 215                if (bo->mem.bus.is_iomem)
 216                        pfn = ((bo->mem.bus.base + bo->mem.bus.offset) >> PAGE_SHIFT) + page_offset;
 217                else {
 218                        page = ttm->pages[page_offset];
 219                        if (unlikely(!page && i == 0)) {
 220                                retval = VM_FAULT_OOM;
 221                                goto out_io_unlock;
 222                        } else if (unlikely(!page)) {
 223                                break;
 224                        }
 225                        page->mapping = vma->vm_file->f_mapping;
 226                        page->index = drm_vma_node_start(&bo->vma_node) +
 227                                page_offset;
 228                        pfn = page_to_pfn(page);
 229                }
 230
 231                if (vma->vm_flags & VM_MIXEDMAP)
 232                        ret = vm_insert_mixed(&cvma, address, pfn);
 233                else
 234                        ret = vm_insert_pfn(&cvma, address, pfn);
 235
 236                /*
 237                 * Somebody beat us to this PTE or prefaulting to
 238                 * an already populated PTE, or prefaulting error.
 239                 */
 240
 241                if (unlikely((ret == -EBUSY) || (ret != 0 && i > 0)))
 242                        break;
 243                else if (unlikely(ret != 0)) {
 244                        retval =
 245                            (ret == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS;
 246                        goto out_io_unlock;
 247                }
 248
 249                address += PAGE_SIZE;
 250                if (unlikely(++page_offset >= page_last))
 251                        break;
 252        }
 253out_io_unlock:
 254        ttm_mem_io_unlock(man);
 255out_unlock:
 256        ttm_bo_unreserve(bo);
 257        return retval;
 258}
 259
 260static void ttm_bo_vm_open(struct vm_area_struct *vma)
 261{
 262        struct ttm_buffer_object *bo =
 263            (struct ttm_buffer_object *)vma->vm_private_data;
 264
 265        WARN_ON(bo->bdev->dev_mapping != vma->vm_file->f_mapping);
 266
 267        (void)ttm_bo_reference(bo);
 268}
 269
 270static void ttm_bo_vm_close(struct vm_area_struct *vma)
 271{
 272        struct ttm_buffer_object *bo = (struct ttm_buffer_object *)vma->vm_private_data;
 273
 274        ttm_bo_unref(&bo);
 275        vma->vm_private_data = NULL;
 276}
 277
 278static const struct vm_operations_struct ttm_bo_vm_ops = {
 279        .fault = ttm_bo_vm_fault,
 280        .open = ttm_bo_vm_open,
 281        .close = ttm_bo_vm_close
 282};
 283
 284static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,
 285                                                  unsigned long offset,
 286                                                  unsigned long pages)
 287{
 288        struct drm_vma_offset_node *node;
 289        struct ttm_buffer_object *bo = NULL;
 290
 291        drm_vma_offset_lock_lookup(&bdev->vma_manager);
 292
 293        node = drm_vma_offset_lookup_locked(&bdev->vma_manager, offset, pages);
 294        if (likely(node)) {
 295                bo = container_of(node, struct ttm_buffer_object, vma_node);
 296                if (!kref_get_unless_zero(&bo->kref))
 297                        bo = NULL;
 298        }
 299
 300        drm_vma_offset_unlock_lookup(&bdev->vma_manager);
 301
 302        if (!bo)
 303                pr_err("Could not find buffer object to map\n");
 304
 305        return bo;
 306}
 307
 308int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
 309                struct ttm_bo_device *bdev)
 310{
 311        struct ttm_bo_driver *driver;
 312        struct ttm_buffer_object *bo;
 313        int ret;
 314
 315        bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma));
 316        if (unlikely(!bo))
 317                return -EINVAL;
 318
 319        driver = bo->bdev->driver;
 320        if (unlikely(!driver->verify_access)) {
 321                ret = -EPERM;
 322                goto out_unref;
 323        }
 324        ret = driver->verify_access(bo, filp);
 325        if (unlikely(ret != 0))
 326                goto out_unref;
 327
 328        vma->vm_ops = &ttm_bo_vm_ops;
 329
 330        /*
 331         * Note: We're transferring the bo reference to
 332         * vma->vm_private_data here.
 333         */
 334
 335        vma->vm_private_data = bo;
 336
 337        /*
 338         * We'd like to use VM_PFNMAP on shared mappings, where
 339         * (vma->vm_flags & VM_SHARED) != 0, for performance reasons,
 340         * but for some reason VM_PFNMAP + x86 PAT + write-combine is very
 341         * bad for performance. Until that has been sorted out, use
 342         * VM_MIXEDMAP on all mappings. See freedesktop.org bug #75719
 343         */
 344        vma->vm_flags |= VM_MIXEDMAP;
 345        vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
 346        return 0;
 347out_unref:
 348        ttm_bo_unref(&bo);
 349        return ret;
 350}
 351EXPORT_SYMBOL(ttm_bo_mmap);
 352
 353int ttm_fbdev_mmap(struct vm_area_struct *vma, struct ttm_buffer_object *bo)
 354{
 355        if (vma->vm_pgoff != 0)
 356                return -EACCES;
 357
 358        vma->vm_ops = &ttm_bo_vm_ops;
 359        vma->vm_private_data = ttm_bo_reference(bo);
 360        vma->vm_flags |= VM_MIXEDMAP;
 361        vma->vm_flags |= VM_IO | VM_DONTEXPAND;
 362        return 0;
 363}
 364EXPORT_SYMBOL(ttm_fbdev_mmap);
 365