1 // SPDX-License-Identifier: GPL-2.0+ 2 /* Copyright (C) 2015-2018 Broadcom */ 3 4 /** 5 * DOC: V3D GEM BO management support 6 * 7 * Compared to VC4 (V3D 2.x), V3D 3.3 introduces an MMU between the 8 * GPU and the bus, allowing us to use shmem objects for our storage 9 * instead of CMA. 10 * 11 * Physically contiguous objects may still be imported to V3D, but the 12 * driver doesn't allocate physically contiguous objects on its own. 13 * Display engines requiring physically contiguous allocations should 14 * look into Mesa's "renderonly" support (as used by the Mesa pl111 15 * driver) for an example of how to integrate with V3D. 16 */ 17 18 #include <linux/dma-buf.h> 19 #include <linux/vmalloc.h> 20 21 #include <drm/drm_print.h> 22 #include <drm/drm_timeout.h> 23 24 #include "v3d_drv.h" 25 #include "uapi/drm/v3d_drm.h" 26 27 static enum drm_gem_object_status v3d_gem_status(struct drm_gem_object *obj) 28 { 29 struct v3d_bo *bo = to_v3d_bo(obj); 30 enum drm_gem_object_status res = 0; 31 32 if (bo->base.pages) 33 res |= DRM_GEM_OBJECT_RESIDENT; 34 35 return res; 36 } 37 38 /* Called DRM core on the last userspace/kernel unreference of the 39 * BO. 40 */ 41 void v3d_free_object(struct drm_gem_object *obj) 42 { 43 struct v3d_dev *v3d = to_v3d_dev(obj->dev); 44 struct v3d_bo *bo = to_v3d_bo(obj); 45 46 if (bo->vaddr) 47 v3d_put_bo_vaddr(bo); 48 49 v3d_mmu_remove_ptes(bo); 50 51 mutex_lock(&v3d->bo_lock); 52 v3d->bo_stats.num_allocated--; 53 v3d->bo_stats.pages_allocated -= obj->size >> V3D_MMU_PAGE_SHIFT; 54 mutex_unlock(&v3d->bo_lock); 55 56 spin_lock(&v3d->mm_lock); 57 drm_mm_remove_node(&bo->node); 58 spin_unlock(&v3d->mm_lock); 59 60 /* GPU execution may have dirtied any pages in the BO. */ 61 bo->base.pages_mark_dirty_on_put = true; 62 63 drm_gem_shmem_free(&bo->base); 64 } 65 66 static const struct drm_gem_object_funcs v3d_gem_funcs = { 67 .free = v3d_free_object, 68 .print_info = drm_gem_shmem_object_print_info, 69 .pin = drm_gem_shmem_object_pin, 70 .unpin = drm_gem_shmem_object_unpin, 71 .get_sg_table = drm_gem_shmem_object_get_sg_table, 72 .vmap = drm_gem_shmem_object_vmap, 73 .vunmap = drm_gem_shmem_object_vunmap, 74 .mmap = drm_gem_shmem_object_mmap, 75 .status = v3d_gem_status, 76 .vm_ops = &drm_gem_shmem_vm_ops, 77 }; 78 79 /* gem_create_object function for allocating a BO struct and doing 80 * early setup. 81 */ 82 struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size) 83 { 84 struct v3d_bo *bo; 85 struct drm_gem_object *obj; 86 87 if (size == 0) 88 return ERR_PTR(-EINVAL); 89 90 bo = kzalloc_obj(*bo); 91 if (!bo) 92 return ERR_PTR(-ENOMEM); 93 obj = &bo->base.base; 94 95 obj->funcs = &v3d_gem_funcs; 96 bo->base.map_wc = true; 97 INIT_LIST_HEAD(&bo->unref_head); 98 99 return &bo->base.base; 100 } 101 102 static int 103 v3d_bo_create_finish(struct drm_gem_object *obj) 104 { 105 struct v3d_dev *v3d = to_v3d_dev(obj->dev); 106 struct v3d_bo *bo = to_v3d_bo(obj); 107 struct sg_table *sgt; 108 u64 align; 109 int ret; 110 111 /* So far we pin the BO in the MMU for its lifetime, so use 112 * shmem's helper for getting a lifetime sgt. 113 */ 114 sgt = drm_gem_shmem_get_pages_sgt(&bo->base); 115 if (IS_ERR(sgt)) 116 return PTR_ERR(sgt); 117 118 if (!drm_gem_get_huge_mnt(obj->dev)) 119 align = SZ_4K; 120 else if (obj->size >= SZ_1M) 121 align = SZ_1M; 122 else if (obj->size >= SZ_64K) 123 align = SZ_64K; 124 else 125 align = SZ_4K; 126 127 spin_lock(&v3d->mm_lock); 128 /* Allocate the object's space in the GPU's page tables. 129 * Inserting PTEs will happen later, but the offset is for the 130 * lifetime of the BO. 131 */ 132 ret = drm_mm_insert_node_generic(&v3d->mm, &bo->node, 133 obj->size >> V3D_MMU_PAGE_SHIFT, 134 align >> V3D_MMU_PAGE_SHIFT, 0, 0); 135 spin_unlock(&v3d->mm_lock); 136 if (ret) 137 return ret; 138 139 /* Track stats for /debug/dri/n/bo_stats. */ 140 mutex_lock(&v3d->bo_lock); 141 v3d->bo_stats.num_allocated++; 142 v3d->bo_stats.pages_allocated += obj->size >> V3D_MMU_PAGE_SHIFT; 143 mutex_unlock(&v3d->bo_lock); 144 145 v3d_mmu_insert_ptes(bo); 146 147 return 0; 148 } 149 150 struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv, 151 size_t unaligned_size) 152 { 153 struct drm_gem_shmem_object *shmem_obj; 154 struct v3d_bo *bo; 155 int ret; 156 157 shmem_obj = drm_gem_shmem_create(dev, unaligned_size); 158 if (IS_ERR(shmem_obj)) 159 return ERR_CAST(shmem_obj); 160 bo = to_v3d_bo(&shmem_obj->base); 161 bo->vaddr = NULL; 162 163 ret = v3d_bo_create_finish(&shmem_obj->base); 164 if (ret) 165 goto free_obj; 166 167 return bo; 168 169 free_obj: 170 drm_gem_shmem_free(shmem_obj); 171 return ERR_PTR(ret); 172 } 173 174 struct drm_gem_object * 175 v3d_prime_import_sg_table(struct drm_device *dev, 176 struct dma_buf_attachment *attach, 177 struct sg_table *sgt) 178 { 179 struct drm_gem_object *obj; 180 int ret; 181 182 obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt); 183 if (IS_ERR(obj)) 184 return obj; 185 186 ret = v3d_bo_create_finish(obj); 187 if (ret) { 188 drm_gem_shmem_free(&to_v3d_bo(obj)->base); 189 return ERR_PTR(ret); 190 } 191 192 return obj; 193 } 194 195 void v3d_get_bo_vaddr(struct v3d_bo *bo) 196 { 197 struct drm_gem_shmem_object *obj = &bo->base; 198 199 bo->vaddr = vmap(obj->pages, obj->base.size >> PAGE_SHIFT, VM_MAP, 200 pgprot_writecombine(PAGE_KERNEL)); 201 } 202 203 void v3d_put_bo_vaddr(struct v3d_bo *bo) 204 { 205 vunmap(bo->vaddr); 206 bo->vaddr = NULL; 207 } 208 209 int v3d_create_bo_ioctl(struct drm_device *dev, void *data, 210 struct drm_file *file_priv) 211 { 212 struct drm_v3d_create_bo *args = data; 213 struct v3d_bo *bo = NULL; 214 int ret; 215 216 if (args->flags != 0) { 217 drm_dbg(dev, "unknown create_bo flags: %d\n", args->flags); 218 return -EINVAL; 219 } 220 221 bo = v3d_bo_create(dev, file_priv, PAGE_ALIGN(args->size)); 222 if (IS_ERR(bo)) 223 return PTR_ERR(bo); 224 225 args->offset = bo->node.start << V3D_MMU_PAGE_SHIFT; 226 227 ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); 228 drm_gem_object_put(&bo->base.base); 229 230 return ret; 231 } 232 233 int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data, 234 struct drm_file *file_priv) 235 { 236 struct drm_v3d_mmap_bo *args = data; 237 struct drm_gem_object *gem_obj; 238 239 if (args->flags != 0) { 240 drm_dbg(dev, "unknown mmap_bo flags: %d\n", args->flags); 241 return -EINVAL; 242 } 243 244 gem_obj = drm_gem_object_lookup(file_priv, args->handle); 245 if (!gem_obj) { 246 drm_dbg(dev, "Failed to look up GEM BO %d\n", args->handle); 247 return -ENOENT; 248 } 249 250 args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); 251 drm_gem_object_put(gem_obj); 252 253 return 0; 254 } 255 256 int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data, 257 struct drm_file *file_priv) 258 { 259 struct drm_v3d_get_bo_offset *args = data; 260 struct drm_gem_object *gem_obj; 261 struct v3d_bo *bo; 262 263 gem_obj = drm_gem_object_lookup(file_priv, args->handle); 264 if (!gem_obj) { 265 drm_dbg(dev, "Failed to look up GEM BO %d\n", args->handle); 266 return -ENOENT; 267 } 268 bo = to_v3d_bo(gem_obj); 269 270 args->offset = bo->node.start << V3D_MMU_PAGE_SHIFT; 271 272 drm_gem_object_put(gem_obj); 273 return 0; 274 } 275 276 int 277 v3d_wait_bo_ioctl(struct drm_device *dev, void *data, 278 struct drm_file *file_priv) 279 { 280 int ret; 281 struct drm_v3d_wait_bo *args = data; 282 ktime_t start = ktime_get(); 283 u64 delta_ns; 284 unsigned long timeout_jiffies = 285 drm_timeout_rel_to_jiffies(args->timeout_ns); 286 287 if (args->pad != 0) 288 return -EINVAL; 289 290 ret = drm_gem_dma_resv_wait(file_priv, args->handle, 291 true, timeout_jiffies); 292 293 /* Decrement the user's timeout, in case we got interrupted 294 * such that the ioctl will be restarted. 295 */ 296 delta_ns = ktime_to_ns(ktime_sub(ktime_get(), start)); 297 if (delta_ns < args->timeout_ns) 298 args->timeout_ns -= delta_ns; 299 else 300 args->timeout_ns = 0; 301 302 /* Asked to wait beyond the jiffy/scheduler precision? */ 303 if (ret == -ETIME && args->timeout_ns) 304 ret = -EAGAIN; 305 306 return ret; 307 } 308