radeon_sa.c revision 1.4
1/* $NetBSD: radeon_sa.c,v 1.4 2021/12/18 23:45:43 riastradh Exp $ */ 2 3/* 4 * Copyright 2011 Red Hat Inc. 5 * All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the 9 * "Software"), to deal in the Software without restriction, including 10 * without limitation the rights to use, copy, modify, merge, publish, 11 * distribute, sub license, and/or sell copies of the Software, and to 12 * permit persons to whom the Software is furnished to do so, subject to 13 * the following conditions: 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 * USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 * The above copyright notice and this permission notice (including the 24 * next paragraph) shall be included in all copies or substantial portions 25 * of the Software. 26 * 27 */ 28/* 29 * Authors: 30 * Jerome Glisse <glisse@freedesktop.org> 31 */ 32/* Algorithm: 33 * 34 * We store the last allocated bo in "hole", we always try to allocate 35 * after the last allocated bo. Principle is that in a linear GPU ring 36 * progression was is after last is the oldest bo we allocated and thus 37 * the first one that should no longer be in use by the GPU. 38 * 39 * If it's not the case we skip over the bo after last to the closest 40 * done bo if such one exist. If none exist and we are not asked to 41 * block we report failure to allocate. 42 * 43 * If we are asked to block we wait on all the oldest fence of all 44 * rings. We just wait for any of those fence to complete. 45 */ 46 47#include <sys/cdefs.h> 48__KERNEL_RCSID(0, "$NetBSD: radeon_sa.c,v 1.4 2021/12/18 23:45:43 riastradh Exp $"); 49 50#include "radeon.h" 51 52static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo); 53static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager); 54 55int radeon_sa_bo_manager_init(struct radeon_device *rdev, 56 struct radeon_sa_manager *sa_manager, 57 unsigned size, u32 align, u32 domain, u32 flags) 58{ 59 int i, r; 60 61#ifdef __NetBSD__ 62 spin_lock_init(&sa_manager->wq_lock); 63 DRM_INIT_WAITQUEUE(&sa_manager->wq, "radsabom"); 64#else 65 init_waitqueue_head(&sa_manager->wq); 66#endif 67 sa_manager->bo = NULL; 68 sa_manager->size = size; 69 sa_manager->domain = domain; 70 sa_manager->align = align; 71 sa_manager->hole = &sa_manager->olist; 72 INIT_LIST_HEAD(&sa_manager->olist); 73 for (i = 0; i < RADEON_NUM_RINGS; ++i) { 74 INIT_LIST_HEAD(&sa_manager->flist[i]); 75 } 76 77 r = radeon_bo_create(rdev, size, align, true, 78 domain, flags, NULL, NULL, &sa_manager->bo); 79 if (r) { 80 dev_err(rdev->dev, "(%d) failed to allocate bo for manager\n", r); 81 return r; 82 } 83 84 return r; 85} 86 87void radeon_sa_bo_manager_fini(struct radeon_device *rdev, 88 struct radeon_sa_manager *sa_manager) 89{ 90 struct radeon_sa_bo *sa_bo, *tmp; 91 92 if (!list_empty(&sa_manager->olist)) { 93 sa_manager->hole = &sa_manager->olist, 94 radeon_sa_bo_try_free(sa_manager); 95 if (!list_empty(&sa_manager->olist)) { 96 dev_err(rdev->dev, "sa_manager is not empty, clearing anyway\n"); 97 } 98 } 99 list_for_each_entry_safe(sa_bo, tmp, &sa_manager->olist, olist) { 100 radeon_sa_bo_remove_locked(sa_bo); 101 } 102 radeon_bo_unref(&sa_manager->bo); 103 sa_manager->size = 0; 104#ifdef __NetBSD__ 105 DRM_DESTROY_WAITQUEUE(&sa_manager->wq); 106 spin_lock_destroy(&sa_manager->wq_lock); 107#endif 108} 109 110int radeon_sa_bo_manager_start(struct radeon_device *rdev, 111 struct radeon_sa_manager *sa_manager) 112{ 113 int r; 114 115 if (sa_manager->bo == NULL) { 116 dev_err(rdev->dev, "no bo for sa manager\n"); 117 return -EINVAL; 118 } 119 120 /* map the buffer */ 121 r = radeon_bo_reserve(sa_manager->bo, false); 122 if (r) { 123 dev_err(rdev->dev, "(%d) failed to reserve manager bo\n", r); 124 return r; 125 } 126 r = radeon_bo_pin(sa_manager->bo, sa_manager->domain, &sa_manager->gpu_addr); 127 if (r) { 128 radeon_bo_unreserve(sa_manager->bo); 129 dev_err(rdev->dev, "(%d) failed to pin manager bo\n", r); 130 return r; 131 } 132 r = radeon_bo_kmap(sa_manager->bo, &sa_manager->cpu_ptr); 133 radeon_bo_unreserve(sa_manager->bo); 134 return r; 135} 136 137int radeon_sa_bo_manager_suspend(struct radeon_device *rdev, 138 struct radeon_sa_manager *sa_manager) 139{ 140 int r; 141 142 if (sa_manager->bo == NULL) { 143 dev_err(rdev->dev, "no bo for sa manager\n"); 144 return -EINVAL; 145 } 146 147 r = radeon_bo_reserve(sa_manager->bo, false); 148 if (!r) { 149 radeon_bo_kunmap(sa_manager->bo); 150 radeon_bo_unpin(sa_manager->bo); 151 radeon_bo_unreserve(sa_manager->bo); 152 } 153 return r; 154} 155 156static void radeon_sa_bo_remove_locked(struct radeon_sa_bo *sa_bo) 157{ 158 struct radeon_sa_manager *sa_manager = sa_bo->manager; 159 if (sa_manager->hole == &sa_bo->olist) { 160 sa_manager->hole = sa_bo->olist.prev; 161 } 162 list_del_init(&sa_bo->olist); 163 list_del_init(&sa_bo->flist); 164 radeon_fence_unref(&sa_bo->fence); 165 kfree(sa_bo); 166} 167 168static void radeon_sa_bo_try_free(struct radeon_sa_manager *sa_manager) 169{ 170 struct radeon_sa_bo *sa_bo, *tmp; 171 172 if (sa_manager->hole->next == &sa_manager->olist) 173 return; 174 175 sa_bo = list_entry(sa_manager->hole->next, struct radeon_sa_bo, olist); 176 list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) { 177 if (sa_bo->fence == NULL || !radeon_fence_signaled(sa_bo->fence)) { 178 return; 179 } 180 radeon_sa_bo_remove_locked(sa_bo); 181 } 182} 183 184static inline unsigned radeon_sa_bo_hole_soffset(struct radeon_sa_manager *sa_manager) 185{ 186 struct list_head *hole = sa_manager->hole; 187 188 if (hole != &sa_manager->olist) { 189 return list_entry(hole, struct radeon_sa_bo, olist)->eoffset; 190 } 191 return 0; 192} 193 194static inline unsigned radeon_sa_bo_hole_eoffset(struct radeon_sa_manager *sa_manager) 195{ 196 struct list_head *hole = sa_manager->hole; 197 198 if (hole->next != &sa_manager->olist) { 199 return list_entry(hole->next, struct radeon_sa_bo, olist)->soffset; 200 } 201 return sa_manager->size; 202} 203 204static bool radeon_sa_bo_try_alloc(struct radeon_sa_manager *sa_manager, 205 struct radeon_sa_bo *sa_bo, 206 unsigned size, unsigned align) 207{ 208 unsigned soffset, eoffset, wasted; 209 210 soffset = radeon_sa_bo_hole_soffset(sa_manager); 211 eoffset = radeon_sa_bo_hole_eoffset(sa_manager); 212 wasted = (align - (soffset % align)) % align; 213 214 if ((eoffset - soffset) >= (size + wasted)) { 215 soffset += wasted; 216 217 sa_bo->manager = sa_manager; 218 sa_bo->soffset = soffset; 219 sa_bo->eoffset = soffset + size; 220 list_add(&sa_bo->olist, sa_manager->hole); 221 INIT_LIST_HEAD(&sa_bo->flist); 222 sa_manager->hole = &sa_bo->olist; 223 return true; 224 } 225 return false; 226} 227 228/** 229 * radeon_sa_event - Check if we can stop waiting 230 * 231 * @sa_manager: pointer to the sa_manager 232 * @size: number of bytes we want to allocate 233 * @align: alignment we need to match 234 * 235 * Check if either there is a fence we can wait for or 236 * enough free memory to satisfy the allocation directly 237 */ 238static bool radeon_sa_event(struct radeon_sa_manager *sa_manager, 239 unsigned size, unsigned align) 240{ 241 unsigned soffset, eoffset, wasted; 242 int i; 243 244 for (i = 0; i < RADEON_NUM_RINGS; ++i) { 245 if (!list_empty(&sa_manager->flist[i])) { 246 return true; 247 } 248 } 249 250 soffset = radeon_sa_bo_hole_soffset(sa_manager); 251 eoffset = radeon_sa_bo_hole_eoffset(sa_manager); 252 wasted = (align - (soffset % align)) % align; 253 254 if ((eoffset - soffset) >= (size + wasted)) { 255 return true; 256 } 257 258 return false; 259} 260 261static bool radeon_sa_bo_next_hole(struct radeon_sa_manager *sa_manager, 262 struct radeon_fence **fences, 263 unsigned *tries) 264{ 265 struct radeon_sa_bo *best_bo = NULL; 266 unsigned i, soffset, best, tmp; 267 268 /* if hole points to the end of the buffer */ 269 if (sa_manager->hole->next == &sa_manager->olist) { 270 /* try again with its beginning */ 271 sa_manager->hole = &sa_manager->olist; 272 return true; 273 } 274 275 soffset = radeon_sa_bo_hole_soffset(sa_manager); 276 /* to handle wrap around we add sa_manager->size */ 277 best = sa_manager->size * 2; 278 /* go over all fence list and try to find the closest sa_bo 279 * of the current last 280 */ 281 for (i = 0; i < RADEON_NUM_RINGS; ++i) { 282 struct radeon_sa_bo *sa_bo; 283 284 if (list_empty(&sa_manager->flist[i])) { 285 continue; 286 } 287 288 sa_bo = list_first_entry(&sa_manager->flist[i], 289 struct radeon_sa_bo, flist); 290 291 if (!radeon_fence_signaled(sa_bo->fence)) { 292 fences[i] = sa_bo->fence; 293 continue; 294 } 295 296 /* limit the number of tries each ring gets */ 297 if (tries[i] > 2) { 298 continue; 299 } 300 301 tmp = sa_bo->soffset; 302 if (tmp < soffset) { 303 /* wrap around, pretend it's after */ 304 tmp += sa_manager->size; 305 } 306 tmp -= soffset; 307 if (tmp < best) { 308 /* this sa bo is the closest one */ 309 best = tmp; 310 best_bo = sa_bo; 311 } 312 } 313 314 if (best_bo) { 315 ++tries[best_bo->fence->ring]; 316 sa_manager->hole = best_bo->olist.prev; 317 318 /* we knew that this one is signaled, 319 so it's save to remote it */ 320 radeon_sa_bo_remove_locked(best_bo); 321 return true; 322 } 323 return false; 324} 325 326int radeon_sa_bo_new(struct radeon_device *rdev, 327 struct radeon_sa_manager *sa_manager, 328 struct radeon_sa_bo **sa_bo, 329 unsigned size, unsigned align) 330{ 331 struct radeon_fence *fences[RADEON_NUM_RINGS]; 332 unsigned tries[RADEON_NUM_RINGS]; 333 int i, r; 334 335 BUG_ON(align > sa_manager->align); 336 BUG_ON(size > sa_manager->size); 337 338 *sa_bo = kmalloc(sizeof(struct radeon_sa_bo), GFP_KERNEL); 339 if ((*sa_bo) == NULL) { 340 return -ENOMEM; 341 } 342 (*sa_bo)->manager = sa_manager; 343 (*sa_bo)->fence = NULL; 344 INIT_LIST_HEAD(&(*sa_bo)->olist); 345 INIT_LIST_HEAD(&(*sa_bo)->flist); 346 347#ifdef __NetBSD__ 348 spin_lock(&sa_manager->wq_lock); 349#else 350 spin_lock(&sa_manager->wq.lock); 351#endif 352 do { 353 for (i = 0; i < RADEON_NUM_RINGS; ++i) { 354 fences[i] = NULL; 355 tries[i] = 0; 356 } 357 358 do { 359 radeon_sa_bo_try_free(sa_manager); 360 361 if (radeon_sa_bo_try_alloc(sa_manager, *sa_bo, 362 size, align)) { 363#ifdef __NetBSD__ 364 spin_unlock(&sa_manager->wq_lock); 365#else 366 spin_unlock(&sa_manager->wq.lock); 367#endif 368 return 0; 369 } 370 371 /* see if we can skip over some allocations */ 372 } while (radeon_sa_bo_next_hole(sa_manager, fences, tries)); 373 374 for (i = 0; i < RADEON_NUM_RINGS; ++i) 375 radeon_fence_ref(fences[i]); 376 377#ifdef __NetBSD__ 378 spin_unlock(&sa_manager->wq_lock); 379 r = radeon_fence_wait_any(rdev, fences, false); 380 for (i = 0; i < RADEON_NUM_RINGS; ++i) 381 radeon_fence_unref(&fences[i]); 382 spin_lock(&sa_manager->wq_lock); 383 /* if we have nothing to wait for block */ 384 if (r == -ENOENT) 385 DRM_SPIN_WAIT_UNTIL(r, &sa_manager->wq, 386 &sa_manager->wq_lock, 387 radeon_sa_event(sa_manager, size, align)); 388#else 389 spin_unlock(&sa_manager->wq.lock); 390 r = radeon_fence_wait_any(rdev, fences, false); 391 for (i = 0; i < RADEON_NUM_RINGS; ++i) 392 radeon_fence_unref(&fences[i]); 393 spin_lock(&sa_manager->wq.lock); 394 /* if we have nothing to wait for block */ 395 if (r == -ENOENT) { 396 r = wait_event_interruptible_locked( 397 sa_manager->wq, 398 radeon_sa_event(sa_manager, size, align) 399 ); 400 } 401#endif 402 403 } while (!r); 404 405#ifdef __NetBSD__ 406 spin_unlock(&sa_manager->wq_lock); 407#else 408 spin_unlock(&sa_manager->wq.lock); 409#endif 410 kfree(*sa_bo); 411 *sa_bo = NULL; 412 return r; 413} 414 415void radeon_sa_bo_free(struct radeon_device *rdev, struct radeon_sa_bo **sa_bo, 416 struct radeon_fence *fence) 417{ 418 struct radeon_sa_manager *sa_manager; 419 420 if (sa_bo == NULL || *sa_bo == NULL) { 421 return; 422 } 423 424 sa_manager = (*sa_bo)->manager; 425#ifdef __NetBSD__ 426 spin_lock(&sa_manager->wq_lock); 427#else 428 spin_lock(&sa_manager->wq.lock); 429#endif 430 if (fence && !radeon_fence_signaled(fence)) { 431 (*sa_bo)->fence = radeon_fence_ref(fence); 432 list_add_tail(&(*sa_bo)->flist, 433 &sa_manager->flist[fence->ring]); 434 } else { 435 radeon_sa_bo_remove_locked(*sa_bo); 436 } 437#ifdef __NetBSD__ 438 DRM_SPIN_WAKEUP_ALL(&sa_manager->wq, &sa_manager->wq_lock); 439 spin_unlock(&sa_manager->wq_lock); 440#else 441 wake_up_all_locked(&sa_manager->wq); 442 spin_unlock(&sa_manager->wq.lock); 443#endif 444 *sa_bo = NULL; 445} 446 447#if defined(CONFIG_DEBUG_FS) 448void radeon_sa_bo_dump_debug_info(struct radeon_sa_manager *sa_manager, 449 struct seq_file *m) 450{ 451 struct radeon_sa_bo *i; 452 453 spin_lock(&sa_manager->wq.lock); 454 list_for_each_entry(i, &sa_manager->olist, olist) { 455 uint64_t soffset = i->soffset + sa_manager->gpu_addr; 456 uint64_t eoffset = i->eoffset + sa_manager->gpu_addr; 457 if (&i->olist == sa_manager->hole) { 458 seq_printf(m, ">"); 459 } else { 460 seq_printf(m, " "); 461 } 462 seq_printf(m, "[0x%010llx 0x%010llx] size %8lld", 463 soffset, eoffset, eoffset - soffset); 464 if (i->fence) { 465 seq_printf(m, " protected by 0x%016llx on ring %d", 466 i->fence->seq, i->fence->ring); 467 } 468 seq_printf(m, "\n"); 469 } 470 spin_unlock(&sa_manager->wq.lock); 471} 472#endif 473