root/drivers/gpu/drm/qxl/qxl_release.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. qxl_get_driver_name
  2. qxl_get_timeline_name
  3. qxl_fence_wait
  4. qxl_release_alloc
  5. qxl_release_free_list
  6. qxl_release_free
  7. qxl_release_bo_alloc
  8. qxl_release_list_add
  9. qxl_release_validate_bo
  10. qxl_release_reserve_list
  11. qxl_release_backoff_reserve_list
  12. qxl_alloc_surface_release_reserved
  13. qxl_alloc_release_reserved
  14. qxl_release_from_id_locked
  15. qxl_release_map
  16. qxl_release_unmap
  17. qxl_release_fence_buffer_objects

   1 /*
   2  * Copyright 2011 Red Hat, Inc.
   3  *
   4  * Permission is hereby granted, free of charge, to any person obtaining a
   5  * copy of this software and associated documentation files (the "Software"),
   6  * to deal in the Software without restriction, including without limitation
   7  * on the rights to use, copy, modify, merge, publish, distribute, sub
   8  * license, and/or sell copies of the Software, and to permit persons to whom
   9  * the Software is furnished to do so, subject to the following conditions:
  10  *
  11  * The above copyright notice and this permission notice (including the next
  12  * paragraph) shall be included in all copies or substantial portions of the
  13  * Software.
  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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21  */
  22 
  23 #include <linux/delay.h>
  24 
  25 #include <trace/events/dma_fence.h>
  26 
  27 #include "qxl_drv.h"
  28 #include "qxl_object.h"
  29 
  30 /*
  31  * drawable cmd cache - allocate a bunch of VRAM pages, suballocate
  32  * into 256 byte chunks for now - gives 16 cmds per page.
  33  *
  34  * use an ida to index into the chunks?
  35  */
  36 /* manage releaseables */
  37 /* stack them 16 high for now -drawable object is 191 */
  38 #define RELEASE_SIZE 256
  39 #define RELEASES_PER_BO (4096 / RELEASE_SIZE)
  40 /* put an alloc/dealloc surface cmd into one bo and round up to 128 */
  41 #define SURFACE_RELEASE_SIZE 128
  42 #define SURFACE_RELEASES_PER_BO (4096 / SURFACE_RELEASE_SIZE)
  43 
  44 static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE };
  45 static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO };
  46 
  47 static const char *qxl_get_driver_name(struct dma_fence *fence)
  48 {
  49         return "qxl";
  50 }
  51 
  52 static const char *qxl_get_timeline_name(struct dma_fence *fence)
  53 {
  54         return "release";
  55 }
  56 
  57 static long qxl_fence_wait(struct dma_fence *fence, bool intr,
  58                            signed long timeout)
  59 {
  60         struct qxl_device *qdev;
  61         struct qxl_release *release;
  62         int count = 0, sc = 0;
  63         bool have_drawable_releases;
  64         unsigned long cur, end = jiffies + timeout;
  65 
  66         qdev = container_of(fence->lock, struct qxl_device, release_lock);
  67         release = container_of(fence, struct qxl_release, base);
  68         have_drawable_releases = release->type == QXL_RELEASE_DRAWABLE;
  69 
  70 retry:
  71         sc++;
  72 
  73         if (dma_fence_is_signaled(fence))
  74                 goto signaled;
  75 
  76         qxl_io_notify_oom(qdev);
  77 
  78         for (count = 0; count < 11; count++) {
  79                 if (!qxl_queue_garbage_collect(qdev, true))
  80                         break;
  81 
  82                 if (dma_fence_is_signaled(fence))
  83                         goto signaled;
  84         }
  85 
  86         if (dma_fence_is_signaled(fence))
  87                 goto signaled;
  88 
  89         if (have_drawable_releases || sc < 4) {
  90                 if (sc > 2)
  91                         /* back off */
  92                         usleep_range(500, 1000);
  93 
  94                 if (time_after(jiffies, end))
  95                         return 0;
  96 
  97                 if (have_drawable_releases && sc > 300) {
  98                         DMA_FENCE_WARN(fence, "failed to wait on release %llu "
  99                                        "after spincount %d\n",
 100                                        fence->context & ~0xf0000000, sc);
 101                         goto signaled;
 102                 }
 103                 goto retry;
 104         }
 105         /*
 106          * yeah, original sync_obj_wait gave up after 3 spins when
 107          * have_drawable_releases is not set.
 108          */
 109 
 110 signaled:
 111         cur = jiffies;
 112         if (time_after(cur, end))
 113                 return 0;
 114         return end - cur;
 115 }
 116 
 117 static const struct dma_fence_ops qxl_fence_ops = {
 118         .get_driver_name = qxl_get_driver_name,
 119         .get_timeline_name = qxl_get_timeline_name,
 120         .wait = qxl_fence_wait,
 121 };
 122 
 123 static int
 124 qxl_release_alloc(struct qxl_device *qdev, int type,
 125                   struct qxl_release **ret)
 126 {
 127         struct qxl_release *release;
 128         int handle;
 129         size_t size = sizeof(*release);
 130 
 131         release = kmalloc(size, GFP_KERNEL);
 132         if (!release) {
 133                 DRM_ERROR("Out of memory\n");
 134                 return -ENOMEM;
 135         }
 136         release->base.ops = NULL;
 137         release->type = type;
 138         release->release_offset = 0;
 139         release->surface_release_id = 0;
 140         INIT_LIST_HEAD(&release->bos);
 141 
 142         idr_preload(GFP_KERNEL);
 143         spin_lock(&qdev->release_idr_lock);
 144         handle = idr_alloc(&qdev->release_idr, release, 1, 0, GFP_NOWAIT);
 145         release->base.seqno = ++qdev->release_seqno;
 146         spin_unlock(&qdev->release_idr_lock);
 147         idr_preload_end();
 148         if (handle < 0) {
 149                 kfree(release);
 150                 *ret = NULL;
 151                 return handle;
 152         }
 153         *ret = release;
 154         DRM_DEBUG_DRIVER("allocated release %d\n", handle);
 155         release->id = handle;
 156         return handle;
 157 }
 158 
 159 static void
 160 qxl_release_free_list(struct qxl_release *release)
 161 {
 162         while (!list_empty(&release->bos)) {
 163                 struct qxl_bo_list *entry;
 164                 struct qxl_bo *bo;
 165 
 166                 entry = container_of(release->bos.next,
 167                                      struct qxl_bo_list, tv.head);
 168                 bo = to_qxl_bo(entry->tv.bo);
 169                 qxl_bo_unref(&bo);
 170                 list_del(&entry->tv.head);
 171                 kfree(entry);
 172         }
 173         release->release_bo = NULL;
 174 }
 175 
 176 void
 177 qxl_release_free(struct qxl_device *qdev,
 178                  struct qxl_release *release)
 179 {
 180         DRM_DEBUG_DRIVER("release %d, type %d\n", release->id, release->type);
 181 
 182         if (release->surface_release_id)
 183                 qxl_surface_id_dealloc(qdev, release->surface_release_id);
 184 
 185         spin_lock(&qdev->release_idr_lock);
 186         idr_remove(&qdev->release_idr, release->id);
 187         spin_unlock(&qdev->release_idr_lock);
 188 
 189         if (release->base.ops) {
 190                 WARN_ON(list_empty(&release->bos));
 191                 qxl_release_free_list(release);
 192 
 193                 dma_fence_signal(&release->base);
 194                 dma_fence_put(&release->base);
 195         } else {
 196                 qxl_release_free_list(release);
 197                 kfree(release);
 198         }
 199 }
 200 
 201 static int qxl_release_bo_alloc(struct qxl_device *qdev,
 202                                 struct qxl_bo **bo)
 203 {
 204         /* pin releases bo's they are too messy to evict */
 205         return qxl_bo_create(qdev, PAGE_SIZE, false, true,
 206                              QXL_GEM_DOMAIN_VRAM, NULL, bo);
 207 }
 208 
 209 int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo)
 210 {
 211         struct qxl_bo_list *entry;
 212 
 213         list_for_each_entry(entry, &release->bos, tv.head) {
 214                 if (entry->tv.bo == &bo->tbo)
 215                         return 0;
 216         }
 217 
 218         entry = kmalloc(sizeof(struct qxl_bo_list), GFP_KERNEL);
 219         if (!entry)
 220                 return -ENOMEM;
 221 
 222         qxl_bo_ref(bo);
 223         entry->tv.bo = &bo->tbo;
 224         entry->tv.num_shared = 0;
 225         list_add_tail(&entry->tv.head, &release->bos);
 226         return 0;
 227 }
 228 
 229 static int qxl_release_validate_bo(struct qxl_bo *bo)
 230 {
 231         struct ttm_operation_ctx ctx = { true, false };
 232         int ret;
 233 
 234         if (!bo->pin_count) {
 235                 qxl_ttm_placement_from_domain(bo, bo->type, false);
 236                 ret = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);
 237                 if (ret)
 238                         return ret;
 239         }
 240 
 241         ret = dma_resv_reserve_shared(bo->tbo.base.resv, 1);
 242         if (ret)
 243                 return ret;
 244 
 245         /* allocate a surface for reserved + validated buffers */
 246         ret = qxl_bo_check_id(bo->tbo.base.dev->dev_private, bo);
 247         if (ret)
 248                 return ret;
 249         return 0;
 250 }
 251 
 252 int qxl_release_reserve_list(struct qxl_release *release, bool no_intr)
 253 {
 254         int ret;
 255         struct qxl_bo_list *entry;
 256 
 257         /* if only one object on the release its the release itself
 258            since these objects are pinned no need to reserve */
 259         if (list_is_singular(&release->bos))
 260                 return 0;
 261 
 262         ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos,
 263                                      !no_intr, NULL, true);
 264         if (ret)
 265                 return ret;
 266 
 267         list_for_each_entry(entry, &release->bos, tv.head) {
 268                 struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
 269 
 270                 ret = qxl_release_validate_bo(bo);
 271                 if (ret) {
 272                         ttm_eu_backoff_reservation(&release->ticket, &release->bos);
 273                         return ret;
 274                 }
 275         }
 276         return 0;
 277 }
 278 
 279 void qxl_release_backoff_reserve_list(struct qxl_release *release)
 280 {
 281         /* if only one object on the release its the release itself
 282            since these objects are pinned no need to reserve */
 283         if (list_is_singular(&release->bos))
 284                 return;
 285 
 286         ttm_eu_backoff_reservation(&release->ticket, &release->bos);
 287 }
 288 
 289 int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
 290                                        enum qxl_surface_cmd_type surface_cmd_type,
 291                                        struct qxl_release *create_rel,
 292                                        struct qxl_release **release)
 293 {
 294         if (surface_cmd_type == QXL_SURFACE_CMD_DESTROY && create_rel) {
 295                 int idr_ret;
 296                 struct qxl_bo *bo;
 297                 union qxl_release_info *info;
 298 
 299                 /* stash the release after the create command */
 300                 idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release);
 301                 if (idr_ret < 0)
 302                         return idr_ret;
 303                 bo = create_rel->release_bo;
 304 
 305                 (*release)->release_bo = bo;
 306                 (*release)->release_offset = create_rel->release_offset + 64;
 307 
 308                 qxl_release_list_add(*release, bo);
 309 
 310                 info = qxl_release_map(qdev, *release);
 311                 info->id = idr_ret;
 312                 qxl_release_unmap(qdev, *release, info);
 313                 return 0;
 314         }
 315 
 316         return qxl_alloc_release_reserved(qdev, sizeof(struct qxl_surface_cmd),
 317                                          QXL_RELEASE_SURFACE_CMD, release, NULL);
 318 }
 319 
 320 int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
 321                                        int type, struct qxl_release **release,
 322                                        struct qxl_bo **rbo)
 323 {
 324         struct qxl_bo *bo;
 325         int idr_ret;
 326         int ret = 0;
 327         union qxl_release_info *info;
 328         int cur_idx;
 329 
 330         if (type == QXL_RELEASE_DRAWABLE)
 331                 cur_idx = 0;
 332         else if (type == QXL_RELEASE_SURFACE_CMD)
 333                 cur_idx = 1;
 334         else if (type == QXL_RELEASE_CURSOR_CMD)
 335                 cur_idx = 2;
 336         else {
 337                 DRM_ERROR("got illegal type: %d\n", type);
 338                 return -EINVAL;
 339         }
 340 
 341         idr_ret = qxl_release_alloc(qdev, type, release);
 342         if (idr_ret < 0) {
 343                 if (rbo)
 344                         *rbo = NULL;
 345                 return idr_ret;
 346         }
 347 
 348         mutex_lock(&qdev->release_mutex);
 349         if (qdev->current_release_bo_offset[cur_idx] + 1 >= releases_per_bo[cur_idx]) {
 350                 qxl_bo_unref(&qdev->current_release_bo[cur_idx]);
 351                 qdev->current_release_bo_offset[cur_idx] = 0;
 352                 qdev->current_release_bo[cur_idx] = NULL;
 353         }
 354         if (!qdev->current_release_bo[cur_idx]) {
 355                 ret = qxl_release_bo_alloc(qdev, &qdev->current_release_bo[cur_idx]);
 356                 if (ret) {
 357                         mutex_unlock(&qdev->release_mutex);
 358                         qxl_release_free(qdev, *release);
 359                         return ret;
 360                 }
 361         }
 362 
 363         bo = qxl_bo_ref(qdev->current_release_bo[cur_idx]);
 364 
 365         (*release)->release_bo = bo;
 366         (*release)->release_offset = qdev->current_release_bo_offset[cur_idx] * release_size_per_bo[cur_idx];
 367         qdev->current_release_bo_offset[cur_idx]++;
 368 
 369         if (rbo)
 370                 *rbo = bo;
 371 
 372         mutex_unlock(&qdev->release_mutex);
 373 
 374         ret = qxl_release_list_add(*release, bo);
 375         qxl_bo_unref(&bo);
 376         if (ret) {
 377                 qxl_release_free(qdev, *release);
 378                 return ret;
 379         }
 380 
 381         info = qxl_release_map(qdev, *release);
 382         info->id = idr_ret;
 383         qxl_release_unmap(qdev, *release, info);
 384 
 385         return ret;
 386 }
 387 
 388 struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev,
 389                                                    uint64_t id)
 390 {
 391         struct qxl_release *release;
 392 
 393         spin_lock(&qdev->release_idr_lock);
 394         release = idr_find(&qdev->release_idr, id);
 395         spin_unlock(&qdev->release_idr_lock);
 396         if (!release) {
 397                 DRM_ERROR("failed to find id in release_idr\n");
 398                 return NULL;
 399         }
 400 
 401         return release;
 402 }
 403 
 404 union qxl_release_info *qxl_release_map(struct qxl_device *qdev,
 405                                         struct qxl_release *release)
 406 {
 407         void *ptr;
 408         union qxl_release_info *info;
 409         struct qxl_bo *bo = release->release_bo;
 410 
 411         ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_MASK);
 412         if (!ptr)
 413                 return NULL;
 414         info = ptr + (release->release_offset & ~PAGE_MASK);
 415         return info;
 416 }
 417 
 418 void qxl_release_unmap(struct qxl_device *qdev,
 419                        struct qxl_release *release,
 420                        union qxl_release_info *info)
 421 {
 422         struct qxl_bo *bo = release->release_bo;
 423         void *ptr;
 424 
 425         ptr = ((void *)info) - (release->release_offset & ~PAGE_MASK);
 426         qxl_bo_kunmap_atomic_page(qdev, bo, ptr);
 427 }
 428 
 429 void qxl_release_fence_buffer_objects(struct qxl_release *release)
 430 {
 431         struct ttm_buffer_object *bo;
 432         struct ttm_bo_global *glob;
 433         struct ttm_bo_device *bdev;
 434         struct ttm_validate_buffer *entry;
 435         struct qxl_device *qdev;
 436 
 437         /* if only one object on the release its the release itself
 438            since these objects are pinned no need to reserve */
 439         if (list_is_singular(&release->bos) || list_empty(&release->bos))
 440                 return;
 441 
 442         bo = list_first_entry(&release->bos, struct ttm_validate_buffer, head)->bo;
 443         bdev = bo->bdev;
 444         qdev = container_of(bdev, struct qxl_device, mman.bdev);
 445 
 446         /*
 447          * Since we never really allocated a context and we don't want to conflict,
 448          * set the highest bits. This will break if we really allow exporting of dma-bufs.
 449          */
 450         dma_fence_init(&release->base, &qxl_fence_ops, &qdev->release_lock,
 451                        release->id | 0xf0000000, release->base.seqno);
 452         trace_dma_fence_emit(&release->base);
 453 
 454         glob = bdev->glob;
 455 
 456         spin_lock(&glob->lru_lock);
 457 
 458         list_for_each_entry(entry, &release->bos, head) {
 459                 bo = entry->bo;
 460 
 461                 dma_resv_add_shared_fence(bo->base.resv, &release->base);
 462                 ttm_bo_add_to_lru(bo);
 463                 dma_resv_unlock(bo->base.resv);
 464         }
 465         spin_unlock(&glob->lru_lock);
 466         ww_acquire_fini(&release->ticket);
 467 }
 468 

/* [<][>][^][v][top][bottom][index][help] */