1/* $NetBSD: qxl_object.c,v 1.3 2021/12/18 23:45:42 riastradh Exp $ */ 2 3/* 4 * Copyright 2013 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Dave Airlie 25 * Alon Levy 26 */ 27 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: qxl_object.c,v 1.3 2021/12/18 23:45:42 riastradh Exp $"); 30 31#include "qxl_drv.h" 32#include "qxl_object.h" 33 34#include <linux/io-mapping.h> 35static void qxl_ttm_bo_destroy(struct ttm_buffer_object *tbo) 36{ 37 struct qxl_bo *bo; 38 struct qxl_device *qdev; 39 40 bo = to_qxl_bo(tbo); 41 qdev = (struct qxl_device *)bo->tbo.base.dev->dev_private; 42 43 qxl_surface_evict(qdev, bo, false); 44 WARN_ON_ONCE(bo->map_count > 0); 45 mutex_lock(&qdev->gem.mutex); 46 list_del_init(&bo->list); 47 mutex_unlock(&qdev->gem.mutex); 48 drm_gem_object_release(&bo->tbo.base); 49 kfree(bo); 50} 51 52bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo) 53{ 54 if (bo->destroy == &qxl_ttm_bo_destroy) 55 return true; 56 return false; 57} 58 59void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned) 60{ 61 u32 c = 0; 62 u32 pflag = 0; 63 unsigned int i; 64 65 if (pinned) 66 pflag |= TTM_PL_FLAG_NO_EVICT; 67 if (qbo->tbo.base.size <= PAGE_SIZE) 68 pflag |= TTM_PL_FLAG_TOPDOWN; 69 70 qbo->placement.placement = qbo->placements; 71 qbo->placement.busy_placement = qbo->placements; 72 if (domain == QXL_GEM_DOMAIN_VRAM) 73 qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag; 74 if (domain == QXL_GEM_DOMAIN_SURFACE) { 75 qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV | pflag; 76 qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag; 77 } 78 if (domain == QXL_GEM_DOMAIN_CPU) 79 qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag; 80 if (!c) 81 qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; 82 qbo->placement.num_placement = c; 83 qbo->placement.num_busy_placement = c; 84 for (i = 0; i < c; ++i) { 85 qbo->placements[i].fpfn = 0; 86 qbo->placements[i].lpfn = 0; 87 } 88} 89 90static const struct drm_gem_object_funcs qxl_object_funcs = { 91 .free = qxl_gem_object_free, 92 .open = qxl_gem_object_open, 93 .close = qxl_gem_object_close, 94 .pin = qxl_gem_prime_pin, 95 .unpin = qxl_gem_prime_unpin, 96 .get_sg_table = qxl_gem_prime_get_sg_table, 97 .vmap = qxl_gem_prime_vmap, 98 .vunmap = qxl_gem_prime_vunmap, 99 .mmap = drm_gem_ttm_mmap, 100 .print_info = drm_gem_ttm_print_info, 101}; 102 103int qxl_bo_create(struct qxl_device *qdev, 104 unsigned long size, bool kernel, bool pinned, u32 domain, 105 struct qxl_surface *surf, 106 struct qxl_bo **bo_ptr) 107{ 108 struct qxl_bo *bo; 109 enum ttm_bo_type type; 110 int r; 111 112 if (kernel) 113 type = ttm_bo_type_kernel; 114 else 115 type = ttm_bo_type_device; 116 *bo_ptr = NULL; 117 bo = kzalloc(sizeof(struct qxl_bo), GFP_KERNEL); 118 if (bo == NULL) 119 return -ENOMEM; 120 size = roundup(size, PAGE_SIZE); 121 r = drm_gem_object_init(&qdev->ddev, &bo->tbo.base, size); 122 if (unlikely(r)) { 123 kfree(bo); 124 return r; 125 } 126 bo->tbo.base.funcs = &qxl_object_funcs; 127 bo->type = domain; 128 bo->pin_count = pinned ? 1 : 0; 129 bo->surface_id = 0; 130 INIT_LIST_HEAD(&bo->list); 131 132 if (surf) 133 bo->surf = *surf; 134 135 qxl_ttm_placement_from_domain(bo, domain, pinned); 136 137 r = ttm_bo_init(&qdev->mman.bdev, &bo->tbo, size, type, 138 &bo->placement, 0, !kernel, size, 139 NULL, NULL, &qxl_ttm_bo_destroy); 140 if (unlikely(r != 0)) { 141 if (r != -ERESTARTSYS) 142 dev_err(qdev->ddev.dev, 143 "object_init failed for (%lu, 0x%08X)\n", 144 size, domain); 145 return r; 146 } 147 *bo_ptr = bo; 148 return 0; 149} 150 151int qxl_bo_kmap(struct qxl_bo *bo, void **ptr) 152{ 153 bool is_iomem; 154 int r; 155 156 if (bo->kptr) { 157 if (ptr) 158 *ptr = bo->kptr; 159 bo->map_count++; 160 return 0; 161 } 162 r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap); 163 if (r) 164 return r; 165 bo->kptr = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); 166 if (ptr) 167 *ptr = bo->kptr; 168 bo->map_count = 1; 169 return 0; 170} 171 172void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev, 173 struct qxl_bo *bo, int page_offset) 174{ 175 void *rptr; 176 int ret; 177 struct io_mapping *map; 178 179 if (bo->tbo.mem.mem_type == TTM_PL_VRAM) 180 map = qdev->vram_mapping; 181 else if (bo->tbo.mem.mem_type == TTM_PL_PRIV) 182 map = qdev->surface_mapping; 183 else 184 goto fallback; 185 186 ret = qxl_ttm_io_mem_reserve(bo->tbo.bdev, &bo->tbo.mem); 187 188 return io_mapping_map_atomic_wc(map, bo->tbo.mem.bus.offset + page_offset); 189fallback: 190 if (bo->kptr) { 191 rptr = bo->kptr + (page_offset * PAGE_SIZE); 192 return rptr; 193 } 194 195 ret = qxl_bo_kmap(bo, &rptr); 196 if (ret) 197 return NULL; 198 199 rptr += page_offset * PAGE_SIZE; 200 return rptr; 201} 202 203void qxl_bo_kunmap(struct qxl_bo *bo) 204{ 205 if (bo->kptr == NULL) 206 return; 207 bo->map_count--; 208 if (bo->map_count > 0) 209 return; 210 bo->kptr = NULL; 211 ttm_bo_kunmap(&bo->kmap); 212} 213 214void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev, 215 struct qxl_bo *bo, void *pmap) 216{ 217 if ((bo->tbo.mem.mem_type != TTM_PL_VRAM) && 218 (bo->tbo.mem.mem_type != TTM_PL_PRIV)) 219 goto fallback; 220 221 io_mapping_unmap_atomic(pmap); 222 return; 223 fallback: 224 qxl_bo_kunmap(bo); 225} 226 227void qxl_bo_unref(struct qxl_bo **bo) 228{ 229 if ((*bo) == NULL) 230 return; 231 232 drm_gem_object_put_unlocked(&(*bo)->tbo.base); 233 *bo = NULL; 234} 235 236struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo) 237{ 238 drm_gem_object_get(&bo->tbo.base); 239 return bo; 240} 241 242static int __qxl_bo_pin(struct qxl_bo *bo) 243{ 244 struct ttm_operation_ctx ctx = { false, false }; 245 struct drm_device *ddev = bo->tbo.base.dev; 246 int r; 247 248 if (bo->pin_count) { 249 bo->pin_count++; 250 return 0; 251 } 252 qxl_ttm_placement_from_domain(bo, bo->type, true); 253 r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); 254 if (likely(r == 0)) { 255 bo->pin_count = 1; 256 } 257 if (unlikely(r != 0)) 258 dev_err(ddev->dev, "%p pin failed\n", bo); 259 return r; 260} 261 262static int __qxl_bo_unpin(struct qxl_bo *bo) 263{ 264 struct ttm_operation_ctx ctx = { false, false }; 265 struct drm_device *ddev = bo->tbo.base.dev; 266 int r, i; 267 268 if (!bo->pin_count) { 269 dev_warn(ddev->dev, "%p unpin not necessary\n", bo); 270 return 0; 271 } 272 bo->pin_count--; 273 if (bo->pin_count) 274 return 0; 275 for (i = 0; i < bo->placement.num_placement; i++) 276 bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; 277 r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); 278 if (unlikely(r != 0)) 279 dev_err(ddev->dev, "%p validate failed for unpin\n", bo); 280 return r; 281} 282 283/* 284 * Reserve the BO before pinning the object. If the BO was reserved 285 * beforehand, use the internal version directly __qxl_bo_pin. 286 * 287 */ 288int qxl_bo_pin(struct qxl_bo *bo) 289{ 290 int r; 291 292 r = qxl_bo_reserve(bo, false); 293 if (r) 294 return r; 295 296 r = __qxl_bo_pin(bo); 297 qxl_bo_unreserve(bo); 298 return r; 299} 300 301/* 302 * Reserve the BO before pinning the object. If the BO was reserved 303 * beforehand, use the internal version directly __qxl_bo_unpin. 304 * 305 */ 306int qxl_bo_unpin(struct qxl_bo *bo) 307{ 308 int r; 309 310 r = qxl_bo_reserve(bo, false); 311 if (r) 312 return r; 313 314 r = __qxl_bo_unpin(bo); 315 qxl_bo_unreserve(bo); 316 return r; 317} 318 319void qxl_bo_force_delete(struct qxl_device *qdev) 320{ 321 struct qxl_bo *bo, *n; 322 323 if (list_empty(&qdev->gem.objects)) 324 return; 325 dev_err(qdev->ddev.dev, "Userspace still has active objects !\n"); 326 list_for_each_entry_safe(bo, n, &qdev->gem.objects, list) { 327 dev_err(qdev->ddev.dev, "%p %p %lu %lu force free\n", 328 &bo->tbo.base, bo, (unsigned long)bo->tbo.base.size, 329 *((unsigned long *)&bo->tbo.base.refcount)); 330 mutex_lock(&qdev->gem.mutex); 331 list_del_init(&bo->list); 332 mutex_unlock(&qdev->gem.mutex); 333 /* this should unref the ttm bo */ 334 drm_gem_object_put_unlocked(&bo->tbo.base); 335 } 336} 337 338int qxl_bo_init(struct qxl_device *qdev) 339{ 340 return qxl_ttm_init(qdev); 341} 342 343void qxl_bo_fini(struct qxl_device *qdev) 344{ 345 qxl_ttm_fini(qdev); 346} 347 348int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo) 349{ 350 int ret; 351 352 if (bo->type == QXL_GEM_DOMAIN_SURFACE && bo->surface_id == 0) { 353 /* allocate a surface id for this surface now */ 354 ret = qxl_surface_id_alloc(qdev, bo); 355 if (ret) 356 return ret; 357 358 ret = qxl_hw_surface_alloc(qdev, bo); 359 if (ret) 360 return ret; 361 } 362 return 0; 363} 364 365int qxl_surf_evict(struct qxl_device *qdev) 366{ 367 return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV); 368} 369 370int qxl_vram_evict(struct qxl_device *qdev) 371{ 372 return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_VRAM); 373} 374