/* * * (C) COPYRIGHT 2012-2015 ARM Limited. All rights reserved. * * This program is free software and is provided to you under the terms of the * GNU General Public License version 2 as published by the Free Software * Foundation, and any use by you of this program is subject to the terms * of such GNU licence. * * A copy of the licence is included with the program, and can also be obtained * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ /** * pl111_drm_vma.c * Implementation of the VM functions for PL111 DRM */ #include #include #include #include #include #include #include #include #include "pl111_drm.h" /* BEGIN drivers/staging/omapdrm/omap_gem_helpers.c */ /** * drm_gem_put_pages - helper to free backing pages for a GEM object * @obj: obj in question * @pages: pages to free */ static void _drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, bool dirty, bool accessed) { int i, npages; struct pl111_gem_bo *bo; npages = obj->size >> PAGE_SHIFT; bo = PL111_BO_FROM_GEM(obj); for (i = 0; i < npages; i++) { if (dirty) set_page_dirty(pages[i]); if (accessed) mark_page_accessed(pages[i]); /* Undo the reference we took when populating the table */ page_cache_release(pages[i]); } drm_free_large(pages); } void put_pages(struct drm_gem_object *obj, struct page **pages) { int i, npages; struct pl111_gem_bo *bo; npages = obj->size >> PAGE_SHIFT; bo = PL111_BO_FROM_GEM(obj); _drm_gem_put_pages(obj, pages, true, true); if (bo->backing_data.shm.dma_addrs) { for (i = 0; i < npages; i++) { /* Filter pages unmapped because of CPU accesses */ if (!bo->backing_data.shm.dma_addrs[i]) continue; if (!dma_mapping_error(obj->dev->dev, bo->backing_data.shm.dma_addrs[i])) { dma_unmap_page(obj->dev->dev, bo->backing_data.shm.dma_addrs[i], PAGE_SIZE, DMA_BIDIRECTIONAL); } } kfree(bo->backing_data.shm.dma_addrs); bo->backing_data.shm.dma_addrs = NULL; } } /** * drm_gem_get_pages - helper to allocate backing pages for a GEM object * @obj: obj in question * @gfpmask: gfp mask of requested pages */ static struct page **_drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) { struct inode *inode; struct address_space *mapping; struct page *p, **pages; int i, npages; /* This is the shared memory object that backs the GEM resource */ inode = obj->filp->f_path.dentry->d_inode; mapping = inode->i_mapping; npages = obj->size >> PAGE_SHIFT; pages = drm_malloc_ab(npages, sizeof(struct page *)); if (pages == NULL) return ERR_PTR(-ENOMEM); gfpmask |= mapping_gfp_mask(mapping); for (i = 0; i < npages; i++) { p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); if (IS_ERR(p)) goto fail; pages[i] = p; /* * There is a hypothetical issue w/ drivers that require * buffer memory in the low 4GB.. if the pages are un- * pinned, and swapped out, they can end up swapped back * in above 4GB. If pages are already in memory, then * shmem_read_mapping_page_gfp will ignore the gfpmask, * even if the already in-memory page disobeys the mask. * * It is only a theoretical issue today, because none of * the devices with this limitation can be populated with * enough memory to trigger the issue. But this BUG_ON() * is here as a reminder in case the problem with * shmem_read_mapping_page_gfp() isn't solved by the time * it does become a real issue. * * See this thread: http://lkml.org/lkml/2011/7/11/238 */ BUG_ON((gfpmask & __GFP_DMA32) && (page_to_pfn(p) >= 0x00100000UL)); } return pages; fail: while (i--) page_cache_release(pages[i]); drm_free_large(pages); return ERR_PTR(PTR_ERR(p)); } struct page **get_pages(struct drm_gem_object *obj) { struct pl111_gem_bo *bo; bo = PL111_BO_FROM_GEM(obj); if (bo->backing_data.shm.pages == NULL) { struct page **p; int npages = obj->size >> PAGE_SHIFT; int i; p = _drm_gem_get_pages(obj, GFP_KERNEL); if (IS_ERR(p)) return ERR_PTR(-ENOMEM); bo->backing_data.shm.pages = p; if (bo->backing_data.shm.dma_addrs == NULL) { bo->backing_data.shm.dma_addrs = kzalloc(npages * sizeof(dma_addr_t), GFP_KERNEL); if (bo->backing_data.shm.dma_addrs == NULL) goto error_out; } if (!(bo->type & PL111_BOT_CACHED)) { for (i = 0; i < npages; ++i) { bo->backing_data.shm.dma_addrs[i] = dma_map_page(obj->dev->dev, p[i], 0, PAGE_SIZE, DMA_BIDIRECTIONAL); if (dma_mapping_error(obj->dev->dev, bo->backing_data.shm.dma_addrs[i])) goto error_out; } } } return bo->backing_data.shm.pages; error_out: put_pages(obj, bo->backing_data.shm.pages); bo->backing_data.shm.pages = NULL; return ERR_PTR(-ENOMEM); } /* END drivers/staging/omapdrm/omap_gem_helpers.c */ int pl111_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct page **pages; unsigned long pfn; struct drm_gem_object *obj = vma->vm_private_data; struct pl111_gem_bo *bo = PL111_BO_FROM_GEM(obj); struct drm_device *dev = obj->dev; int ret; mutex_lock(&dev->struct_mutex); /* * Our mmap calls setup a valid vma->vm_pgoff * so we can use vmf->pgoff */ if (bo->type & PL111_BOT_DMA) { pfn = (bo->backing_data.dma.fb_dev_addr >> PAGE_SHIFT) + vmf->pgoff; } else { /* PL111_BOT_SHM */ pages = get_pages(obj); if (IS_ERR(pages)) { dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages)); ret = PTR_ERR(pages); goto error; } pfn = page_to_pfn(pages[vmf->pgoff]); pl111_gem_sync_to_cpu(bo, vmf->pgoff); } DRM_DEBUG("bo=%p physaddr=0x%.8x for offset 0x%x\n", bo, PFN_PHYS(pfn), PFN_PHYS(vmf->pgoff)); ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); error: mutex_unlock(&dev->struct_mutex); switch (ret) { case 0: case -ERESTARTSYS: case -EINTR: case -EBUSY: return VM_FAULT_NOPAGE; case -ENOMEM: return VM_FAULT_OOM; default: return VM_FAULT_SIGBUS; } } /* * The core drm_vm_ functions in kernel 3.4 are not ready * to handle dma_buf cases where vma->vm_file->private_data * cannot be accessed to get the device. * * We use these functions from 3.5 instead where the device * pointer is passed explicitly. * * However they aren't exported from the kernel until 3.10 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)) void pl111_drm_vm_open_locked(struct drm_device *dev, struct vm_area_struct *vma) { struct drm_vma_entry *vma_entry; DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); atomic_inc(&dev->vma_count); vma_entry = kmalloc(sizeof(*vma_entry), GFP_KERNEL); if (vma_entry) { vma_entry->vma = vma; vma_entry->pid = current->pid; list_add(&vma_entry->head, &dev->vmalist); } } void pl111_drm_vm_close_locked(struct drm_device *dev, struct vm_area_struct *vma) { struct drm_vma_entry *pt, *temp; DRM_DEBUG("0x%08lx,0x%08lx\n", vma->vm_start, vma->vm_end - vma->vm_start); atomic_dec(&dev->vma_count); list_for_each_entry_safe(pt, temp, &dev->vmalist, head) { if (pt->vma == vma) { list_del(&pt->head); kfree(pt); break; } } } void pl111_gem_vm_open(struct vm_area_struct *vma) { struct drm_gem_object *obj = vma->vm_private_data; drm_gem_object_reference(obj); mutex_lock(&obj->dev->struct_mutex); pl111_drm_vm_open_locked(obj->dev, vma); mutex_unlock(&obj->dev->struct_mutex); } void pl111_gem_vm_close(struct vm_area_struct *vma) { struct drm_gem_object *obj = vma->vm_private_data; struct drm_device *dev = obj->dev; mutex_lock(&dev->struct_mutex); pl111_drm_vm_close_locked(obj->dev, vma); drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); } #endif