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 "v3d_drv.h" 22 #include "uapi/drm/v3d_drm.h" 23 24 static enum drm_gem_object_status v3d_gem_status(struct drm_gem_object *obj) 25 { 26 struct v3d_bo *bo = to_v3d_bo(obj); 27 enum drm_gem_object_status res = 0; 28 29 if (bo->base.pages) 30 res |= DRM_GEM_OBJECT_RESIDENT; 31 32 return res; 33 } 34 35 /* Called DRM core on the last userspace/kernel unreference of the 36 * BO. 37 */ 38 void v3d_free_object(struct drm_gem_object *obj) 39 { 40 struct v3d_dev *v3d = to_v3d_dev(obj->dev); 41 struct v3d_bo *bo = to_v3d_bo(obj); 42 43 if (bo->vaddr) 44 v3d_put_bo_vaddr(bo); 45 46 v3d_mmu_remove_ptes(bo); 47 48 mutex_lock(&v3d->bo_lock); 49 v3d->bo_stats.num_allocated--; 50 v3d->bo_stats.pages_allocated -= obj->size >> V3D_MMU_PAGE_SHIFT; 51 mutex_unlock(&v3d->bo_lock); 52 53 spin_lock(&v3d->mm_lock); 54 drm_mm_remove_node(&bo->node); 55 spin_unlock(&v3d->mm_lock); 56 57 /* GPU execution may have dirtied any pages in the BO. */ 58 bo->base.pages_mark_dirty_on_put = true; 59 60 drm_gem_shmem_free(&bo->base); 61 } 62 63 static const struct drm_gem_object_funcs v3d_gem_funcs = { 64 .free = v3d_free_object, 65 .print_info = drm_gem_shmem_object_print_info, 66 .pin = drm_gem_shmem_object_pin, 67 .unpin = drm_gem_shmem_object_unpin, 68 .get_sg_table = drm_gem_shmem_object_get_sg_table, 69 .vmap = drm_gem_shmem_object_vmap, 70 .vunmap = drm_gem_shmem_object_vunmap, 71 .mmap = drm_gem_shmem_object_mmap, 72 .status = v3d_gem_status, 73 .vm_ops = &drm_gem_shmem_vm_ops, 74 }; 75 76 /* gem_create_object function for allocating a BO struct and doing 77 * early setup. 78 */ 79 struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size) 80 { 81 struct v3d_bo *bo; 82 struct drm_gem_object *obj; 83 84 if (size == 0) 85 return ERR_PTR(-EINVAL); 86 87 bo = kzalloc(sizeof(*bo), GFP_KERNEL); 88 if (!bo) 89 return ERR_PTR(-ENOMEM); 90 obj = &bo->base.base; 91 92 obj->funcs = &v3d_gem_funcs; 93 bo->base.map_wc = true; 94 INIT_LIST_HEAD(&bo->unref_head); 95 96 return &bo->base.base; 97 } 98 99 static int 100 v3d_bo_create_finish(struct drm_gem_object *obj) 101 { 102 struct v3d_dev *v3d = to_v3d_dev(obj->dev); 103 struct v3d_bo *bo = to_v3d_bo(obj); 104 struct sg_table *sgt; 105 u64 align; 106 int ret; 107 108 /* So far we pin the BO in the MMU for its lifetime, so use 109 * shmem's helper for getting a lifetime sgt. 110 */ 111 sgt = drm_gem_shmem_get_pages_sgt(&bo->base); 112 if (IS_ERR(sgt)) 113 return PTR_ERR(sgt); 114 115 if (!v3d->gemfs) 116 align = SZ_4K; 117 else if (obj->size >= SZ_1M) 118 align = SZ_1M; 119 else if (obj->size >= SZ_64K) 120 align = SZ_64K; 121 else 122 align = SZ_4K; 123 124 spin_lock(&v3d->mm_lock); 125 /* Allocate the object's space in the GPU's page tables. 126 * Inserting PTEs will happen later, but the offset is for the 127 * lifetime of the BO. 128 */ 129 ret = drm_mm_insert_node_generic(&v3d->mm, &bo->node, 130 obj->size >> V3D_MMU_PAGE_SHIFT, 131 align >> V3D_MMU_PAGE_SHIFT, 0, 0); 132 spin_unlock(&v3d->mm_lock); 133 if (ret) 134 return ret; 135 136 /* Track stats for /debug/dri/n/bo_stats. */ 137 mutex_lock(&v3d->bo_lock); 138 v3d->bo_stats.num_allocated++; 139 v3d->bo_stats.pages_allocated += obj->size >> V3D_MMU_PAGE_SHIFT; 140 mutex_unlock(&v3d->bo_lock); 141 142 v3d_mmu_insert_ptes(bo); 143 144 return 0; 145 } 146 147 struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv, 148 size_t unaligned_size) 149 { 150 struct drm_gem_shmem_object *shmem_obj; 151 struct v3d_dev *v3d = to_v3d_dev(dev); 152 struct v3d_bo *bo; 153 int ret; 154 155 shmem_obj = drm_gem_shmem_create_with_mnt(dev, unaligned_size, 156 v3d->gemfs); 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_INFO("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_INFO("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_DEBUG("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_DEBUG("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