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