| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
|---|
| 2 | 2 | |
|---|
| 3 | +#include <linux/dma-buf.h> |
|---|
| 3 | 4 | #include <linux/shmem_fs.h> |
|---|
| 5 | +#include <linux/vmalloc.h> |
|---|
| 6 | +#include <drm/drm_prime.h> |
|---|
| 4 | 7 | |
|---|
| 5 | 8 | #include "vkms_drv.h" |
|---|
| 6 | 9 | |
|---|
| .. | .. |
|---|
| 31 | 34 | struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object, |
|---|
| 32 | 35 | gem); |
|---|
| 33 | 36 | |
|---|
| 34 | | - kvfree(gem->pages); |
|---|
| 37 | + WARN_ON(gem->pages); |
|---|
| 38 | + WARN_ON(gem->vaddr); |
|---|
| 39 | + |
|---|
| 35 | 40 | mutex_destroy(&gem->pages_lock); |
|---|
| 36 | 41 | drm_gem_object_release(obj); |
|---|
| 37 | 42 | kfree(gem); |
|---|
| 38 | 43 | } |
|---|
| 39 | 44 | |
|---|
| 40 | | -int vkms_gem_fault(struct vm_fault *vmf) |
|---|
| 45 | +vm_fault_t vkms_gem_fault(struct vm_fault *vmf) |
|---|
| 41 | 46 | { |
|---|
| 42 | 47 | struct vm_area_struct *vma = vmf->vma; |
|---|
| 43 | 48 | struct vkms_gem_object *obj = vma->vm_private_data; |
|---|
| 44 | 49 | unsigned long vaddr = vmf->address; |
|---|
| 45 | 50 | pgoff_t page_offset; |
|---|
| 46 | 51 | loff_t num_pages; |
|---|
| 47 | | - int ret; |
|---|
| 52 | + vm_fault_t ret = VM_FAULT_SIGBUS; |
|---|
| 48 | 53 | |
|---|
| 49 | 54 | page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT; |
|---|
| 50 | 55 | num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE); |
|---|
| .. | .. |
|---|
| 52 | 57 | if (page_offset > num_pages) |
|---|
| 53 | 58 | return VM_FAULT_SIGBUS; |
|---|
| 54 | 59 | |
|---|
| 55 | | - ret = -ENOENT; |
|---|
| 56 | 60 | mutex_lock(&obj->pages_lock); |
|---|
| 57 | 61 | if (obj->pages) { |
|---|
| 58 | 62 | get_page(obj->pages[page_offset]); |
|---|
| .. | .. |
|---|
| 137 | 141 | args->size = gem_obj->size; |
|---|
| 138 | 142 | args->pitch = pitch; |
|---|
| 139 | 143 | |
|---|
| 140 | | - drm_gem_object_put_unlocked(gem_obj); |
|---|
| 144 | + drm_gem_object_put(gem_obj); |
|---|
| 141 | 145 | |
|---|
| 142 | 146 | DRM_DEBUG_DRIVER("Created object of size %lld\n", size); |
|---|
| 143 | 147 | |
|---|
| 144 | 148 | return 0; |
|---|
| 145 | 149 | } |
|---|
| 146 | 150 | |
|---|
| 147 | | -int vkms_dumb_map(struct drm_file *file, struct drm_device *dev, |
|---|
| 148 | | - u32 handle, u64 *offset) |
|---|
| 151 | +static struct page **_get_pages(struct vkms_gem_object *vkms_obj) |
|---|
| 149 | 152 | { |
|---|
| 150 | | - struct drm_gem_object *obj; |
|---|
| 151 | | - int ret; |
|---|
| 153 | + struct drm_gem_object *gem_obj = &vkms_obj->gem; |
|---|
| 152 | 154 | |
|---|
| 153 | | - obj = drm_gem_object_lookup(file, handle); |
|---|
| 154 | | - if (!obj) |
|---|
| 155 | | - return -ENOENT; |
|---|
| 155 | + if (!vkms_obj->pages) { |
|---|
| 156 | + struct page **pages = drm_gem_get_pages(gem_obj); |
|---|
| 156 | 157 | |
|---|
| 157 | | - if (!obj->filp) { |
|---|
| 158 | | - ret = -EINVAL; |
|---|
| 159 | | - goto unref; |
|---|
| 158 | + if (IS_ERR(pages)) |
|---|
| 159 | + return pages; |
|---|
| 160 | + |
|---|
| 161 | + if (cmpxchg(&vkms_obj->pages, NULL, pages)) |
|---|
| 162 | + drm_gem_put_pages(gem_obj, pages, false, true); |
|---|
| 160 | 163 | } |
|---|
| 161 | 164 | |
|---|
| 162 | | - ret = drm_gem_create_mmap_offset(obj); |
|---|
| 163 | | - if (ret) |
|---|
| 164 | | - goto unref; |
|---|
| 165 | + return vkms_obj->pages; |
|---|
| 166 | +} |
|---|
| 165 | 167 | |
|---|
| 166 | | - *offset = drm_vma_node_offset_addr(&obj->vma_node); |
|---|
| 167 | | -unref: |
|---|
| 168 | | - drm_gem_object_put_unlocked(obj); |
|---|
| 168 | +void vkms_gem_vunmap(struct drm_gem_object *obj) |
|---|
| 169 | +{ |
|---|
| 170 | + struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(obj); |
|---|
| 169 | 171 | |
|---|
| 172 | + mutex_lock(&vkms_obj->pages_lock); |
|---|
| 173 | + if (vkms_obj->vmap_count < 1) { |
|---|
| 174 | + WARN_ON(vkms_obj->vaddr); |
|---|
| 175 | + WARN_ON(vkms_obj->pages); |
|---|
| 176 | + mutex_unlock(&vkms_obj->pages_lock); |
|---|
| 177 | + return; |
|---|
| 178 | + } |
|---|
| 179 | + |
|---|
| 180 | + vkms_obj->vmap_count--; |
|---|
| 181 | + |
|---|
| 182 | + if (vkms_obj->vmap_count == 0) { |
|---|
| 183 | + vunmap(vkms_obj->vaddr); |
|---|
| 184 | + vkms_obj->vaddr = NULL; |
|---|
| 185 | + drm_gem_put_pages(obj, vkms_obj->pages, false, true); |
|---|
| 186 | + vkms_obj->pages = NULL; |
|---|
| 187 | + } |
|---|
| 188 | + |
|---|
| 189 | + mutex_unlock(&vkms_obj->pages_lock); |
|---|
| 190 | +} |
|---|
| 191 | + |
|---|
| 192 | +int vkms_gem_vmap(struct drm_gem_object *obj) |
|---|
| 193 | +{ |
|---|
| 194 | + struct vkms_gem_object *vkms_obj = drm_gem_to_vkms_gem(obj); |
|---|
| 195 | + int ret = 0; |
|---|
| 196 | + |
|---|
| 197 | + mutex_lock(&vkms_obj->pages_lock); |
|---|
| 198 | + |
|---|
| 199 | + if (!vkms_obj->vaddr) { |
|---|
| 200 | + unsigned int n_pages = obj->size >> PAGE_SHIFT; |
|---|
| 201 | + struct page **pages = _get_pages(vkms_obj); |
|---|
| 202 | + |
|---|
| 203 | + if (IS_ERR(pages)) { |
|---|
| 204 | + ret = PTR_ERR(pages); |
|---|
| 205 | + goto out; |
|---|
| 206 | + } |
|---|
| 207 | + |
|---|
| 208 | + vkms_obj->vaddr = vmap(pages, n_pages, VM_MAP, PAGE_KERNEL); |
|---|
| 209 | + if (!vkms_obj->vaddr) |
|---|
| 210 | + goto err_vmap; |
|---|
| 211 | + } |
|---|
| 212 | + |
|---|
| 213 | + vkms_obj->vmap_count++; |
|---|
| 214 | + goto out; |
|---|
| 215 | + |
|---|
| 216 | +err_vmap: |
|---|
| 217 | + ret = -ENOMEM; |
|---|
| 218 | + drm_gem_put_pages(obj, vkms_obj->pages, false, true); |
|---|
| 219 | + vkms_obj->pages = NULL; |
|---|
| 220 | +out: |
|---|
| 221 | + mutex_unlock(&vkms_obj->pages_lock); |
|---|
| 170 | 222 | return ret; |
|---|
| 171 | 223 | } |
|---|
| 224 | + |
|---|
| 225 | +struct drm_gem_object * |
|---|
| 226 | +vkms_prime_import_sg_table(struct drm_device *dev, |
|---|
| 227 | + struct dma_buf_attachment *attach, |
|---|
| 228 | + struct sg_table *sg) |
|---|
| 229 | +{ |
|---|
| 230 | + struct vkms_gem_object *obj; |
|---|
| 231 | + int npages; |
|---|
| 232 | + |
|---|
| 233 | + obj = __vkms_gem_create(dev, attach->dmabuf->size); |
|---|
| 234 | + if (IS_ERR(obj)) |
|---|
| 235 | + return ERR_CAST(obj); |
|---|
| 236 | + |
|---|
| 237 | + npages = PAGE_ALIGN(attach->dmabuf->size) / PAGE_SIZE; |
|---|
| 238 | + DRM_DEBUG_PRIME("Importing %d pages\n", npages); |
|---|
| 239 | + |
|---|
| 240 | + obj->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); |
|---|
| 241 | + if (!obj->pages) { |
|---|
| 242 | + vkms_gem_free_object(&obj->gem); |
|---|
| 243 | + return ERR_PTR(-ENOMEM); |
|---|
| 244 | + } |
|---|
| 245 | + |
|---|
| 246 | + drm_prime_sg_to_page_addr_arrays(sg, obj->pages, NULL, npages); |
|---|
| 247 | + return &obj->gem; |
|---|
| 248 | +} |
|---|