linux/drivers/gpu/drm/i915/selftests/i915_gem_object.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2016 Intel Corporation
   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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
  18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21 * IN THE SOFTWARE.
  22 *
  23 */
  24
  25#include "../i915_selftest.h"
  26
  27#include "mock_gem_device.h"
  28#include "huge_gem_object.h"
  29
  30static int igt_gem_object(void *arg)
  31{
  32        struct drm_i915_private *i915 = arg;
  33        struct drm_i915_gem_object *obj;
  34        int err = -ENOMEM;
  35
  36        /* Basic test to ensure we can create an object */
  37
  38        obj = i915_gem_object_create(i915, PAGE_SIZE);
  39        if (IS_ERR(obj)) {
  40                err = PTR_ERR(obj);
  41                pr_err("i915_gem_object_create failed, err=%d\n", err);
  42                goto out;
  43        }
  44
  45        err = 0;
  46        i915_gem_object_put(obj);
  47out:
  48        return err;
  49}
  50
  51static int igt_phys_object(void *arg)
  52{
  53        struct drm_i915_private *i915 = arg;
  54        struct drm_i915_gem_object *obj;
  55        int err;
  56
  57        /* Create an object and bind it to a contiguous set of physical pages,
  58         * i.e. exercise the i915_gem_object_phys API.
  59         */
  60
  61        obj = i915_gem_object_create(i915, PAGE_SIZE);
  62        if (IS_ERR(obj)) {
  63                err = PTR_ERR(obj);
  64                pr_err("i915_gem_object_create failed, err=%d\n", err);
  65                goto out;
  66        }
  67
  68        mutex_lock(&i915->drm.struct_mutex);
  69        err = i915_gem_object_attach_phys(obj, PAGE_SIZE);
  70        mutex_unlock(&i915->drm.struct_mutex);
  71        if (err) {
  72                pr_err("i915_gem_object_attach_phys failed, err=%d\n", err);
  73                goto out_obj;
  74        }
  75
  76        if (obj->ops != &i915_gem_phys_ops) {
  77                pr_err("i915_gem_object_attach_phys did not create a phys object\n");
  78                err = -EINVAL;
  79                goto out_obj;
  80        }
  81
  82        if (!atomic_read(&obj->mm.pages_pin_count)) {
  83                pr_err("i915_gem_object_attach_phys did not pin its phys pages\n");
  84                err = -EINVAL;
  85                goto out_obj;
  86        }
  87
  88        /* Make the object dirty so that put_pages must do copy back the data */
  89        mutex_lock(&i915->drm.struct_mutex);
  90        err = i915_gem_object_set_to_gtt_domain(obj, true);
  91        mutex_unlock(&i915->drm.struct_mutex);
  92        if (err) {
  93                pr_err("i915_gem_object_set_to_gtt_domain failed with err=%d\n",
  94                       err);
  95                goto out_obj;
  96        }
  97
  98out_obj:
  99        i915_gem_object_put(obj);
 100out:
 101        return err;
 102}
 103
 104static int igt_gem_huge(void *arg)
 105{
 106        const unsigned int nreal = 509; /* just to be awkward */
 107        struct drm_i915_private *i915 = arg;
 108        struct drm_i915_gem_object *obj;
 109        unsigned int n;
 110        int err;
 111
 112        /* Basic sanitycheck of our huge fake object allocation */
 113
 114        obj = huge_gem_object(i915,
 115                              nreal * PAGE_SIZE,
 116                              i915->ggtt.base.total + PAGE_SIZE);
 117        if (IS_ERR(obj))
 118                return PTR_ERR(obj);
 119
 120        err = i915_gem_object_pin_pages(obj);
 121        if (err) {
 122                pr_err("Failed to allocate %u pages (%lu total), err=%d\n",
 123                       nreal, obj->base.size / PAGE_SIZE, err);
 124                goto out;
 125        }
 126
 127        for (n = 0; n < obj->base.size / PAGE_SIZE; n++) {
 128                if (i915_gem_object_get_page(obj, n) !=
 129                    i915_gem_object_get_page(obj, n % nreal)) {
 130                        pr_err("Page lookup mismatch at index %u [%u]\n",
 131                               n, n % nreal);
 132                        err = -EINVAL;
 133                        goto out_unpin;
 134                }
 135        }
 136
 137out_unpin:
 138        i915_gem_object_unpin_pages(obj);
 139out:
 140        i915_gem_object_put(obj);
 141        return err;
 142}
 143
 144struct tile {
 145        unsigned int width;
 146        unsigned int height;
 147        unsigned int stride;
 148        unsigned int size;
 149        unsigned int tiling;
 150        unsigned int swizzle;
 151};
 152
 153static u64 swizzle_bit(unsigned int bit, u64 offset)
 154{
 155        return (offset & BIT_ULL(bit)) >> (bit - 6);
 156}
 157
 158static u64 tiled_offset(const struct tile *tile, u64 v)
 159{
 160        u64 x, y;
 161
 162        if (tile->tiling == I915_TILING_NONE)
 163                return v;
 164
 165        y = div64_u64_rem(v, tile->stride, &x);
 166        v = div64_u64_rem(y, tile->height, &y) * tile->stride * tile->height;
 167
 168        if (tile->tiling == I915_TILING_X) {
 169                v += y * tile->width;
 170                v += div64_u64_rem(x, tile->width, &x) << tile->size;
 171                v += x;
 172        } else {
 173                const unsigned int ytile_span = 16;
 174                const unsigned int ytile_height = 32 * ytile_span;
 175
 176                v += y * ytile_span;
 177                v += div64_u64_rem(x, ytile_span, &x) * ytile_height;
 178                v += x;
 179        }
 180
 181        switch (tile->swizzle) {
 182        case I915_BIT_6_SWIZZLE_9:
 183                v ^= swizzle_bit(9, v);
 184                break;
 185        case I915_BIT_6_SWIZZLE_9_10:
 186                v ^= swizzle_bit(9, v) ^ swizzle_bit(10, v);
 187                break;
 188        case I915_BIT_6_SWIZZLE_9_11:
 189                v ^= swizzle_bit(9, v) ^ swizzle_bit(11, v);
 190                break;
 191        case I915_BIT_6_SWIZZLE_9_10_11:
 192                v ^= swizzle_bit(9, v) ^ swizzle_bit(10, v) ^ swizzle_bit(11, v);
 193                break;
 194        }
 195
 196        return v;
 197}
 198
 199static int check_partial_mapping(struct drm_i915_gem_object *obj,
 200                                 const struct tile *tile,
 201                                 unsigned long end_time)
 202{
 203        const unsigned int nreal = obj->scratch / PAGE_SIZE;
 204        const unsigned long npages = obj->base.size / PAGE_SIZE;
 205        struct i915_vma *vma;
 206        unsigned long page;
 207        int err;
 208
 209        if (igt_timeout(end_time,
 210                        "%s: timed out before tiling=%d stride=%d\n",
 211                        __func__, tile->tiling, tile->stride))
 212                return -EINTR;
 213
 214        err = i915_gem_object_set_tiling(obj, tile->tiling, tile->stride);
 215        if (err) {
 216                pr_err("Failed to set tiling mode=%u, stride=%u, err=%d\n",
 217                       tile->tiling, tile->stride, err);
 218                return err;
 219        }
 220
 221        GEM_BUG_ON(i915_gem_object_get_tiling(obj) != tile->tiling);
 222        GEM_BUG_ON(i915_gem_object_get_stride(obj) != tile->stride);
 223
 224        for_each_prime_number_from(page, 1, npages) {
 225                struct i915_ggtt_view view =
 226                        compute_partial_view(obj, page, MIN_CHUNK_PAGES);
 227                u32 __iomem *io;
 228                struct page *p;
 229                unsigned int n;
 230                u64 offset;
 231                u32 *cpu;
 232
 233                GEM_BUG_ON(view.partial.size > nreal);
 234
 235                err = i915_gem_object_set_to_gtt_domain(obj, true);
 236                if (err) {
 237                        pr_err("Failed to flush to GTT write domain; err=%d\n",
 238                               err);
 239                        return err;
 240                }
 241
 242                vma = i915_gem_object_ggtt_pin(obj, &view, 0, 0, PIN_MAPPABLE);
 243                if (IS_ERR(vma)) {
 244                        pr_err("Failed to pin partial view: offset=%lu; err=%d\n",
 245                               page, (int)PTR_ERR(vma));
 246                        return PTR_ERR(vma);
 247                }
 248
 249                n = page - view.partial.offset;
 250                GEM_BUG_ON(n >= view.partial.size);
 251
 252                io = i915_vma_pin_iomap(vma);
 253                i915_vma_unpin(vma);
 254                if (IS_ERR(io)) {
 255                        pr_err("Failed to iomap partial view: offset=%lu; err=%d\n",
 256                               page, (int)PTR_ERR(io));
 257                        return PTR_ERR(io);
 258                }
 259
 260                iowrite32(page, io + n * PAGE_SIZE/sizeof(*io));
 261                i915_vma_unpin_iomap(vma);
 262
 263                offset = tiled_offset(tile, page << PAGE_SHIFT);
 264                if (offset >= obj->base.size)
 265                        continue;
 266
 267                flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
 268
 269                p = i915_gem_object_get_page(obj, offset >> PAGE_SHIFT);
 270                cpu = kmap(p) + offset_in_page(offset);
 271                drm_clflush_virt_range(cpu, sizeof(*cpu));
 272                if (*cpu != (u32)page) {
 273                        pr_err("Partial view for %lu [%u] (offset=%llu, size=%u [%llu, row size %u], fence=%d, tiling=%d, stride=%d) misalignment, expected write to page (%llu + %u [0x%llx]) of 0x%x, found 0x%x\n",
 274                               page, n,
 275                               view.partial.offset,
 276                               view.partial.size,
 277                               vma->size >> PAGE_SHIFT,
 278                               tile_row_pages(obj),
 279                               vma->fence ? vma->fence->id : -1, tile->tiling, tile->stride,
 280                               offset >> PAGE_SHIFT,
 281                               (unsigned int)offset_in_page(offset),
 282                               offset,
 283                               (u32)page, *cpu);
 284                        err = -EINVAL;
 285                }
 286                *cpu = 0;
 287                drm_clflush_virt_range(cpu, sizeof(*cpu));
 288                kunmap(p);
 289                if (err)
 290                        return err;
 291        }
 292
 293        return 0;
 294}
 295
 296static int igt_partial_tiling(void *arg)
 297{
 298        const unsigned int nreal = 1 << 12; /* largest tile row x2 */
 299        struct drm_i915_private *i915 = arg;
 300        struct drm_i915_gem_object *obj;
 301        int tiling;
 302        int err;
 303
 304        /* We want to check the page mapping and fencing of a large object
 305         * mmapped through the GTT. The object we create is larger than can
 306         * possibly be mmaped as a whole, and so we must use partial GGTT vma.
 307         * We then check that a write through each partial GGTT vma ends up
 308         * in the right set of pages within the object, and with the expected
 309         * tiling, which we verify by manual swizzling.
 310         */
 311
 312        obj = huge_gem_object(i915,
 313                              nreal << PAGE_SHIFT,
 314                              (1 + next_prime_number(i915->ggtt.base.total >> PAGE_SHIFT)) << PAGE_SHIFT);
 315        if (IS_ERR(obj))
 316                return PTR_ERR(obj);
 317
 318        err = i915_gem_object_pin_pages(obj);
 319        if (err) {
 320                pr_err("Failed to allocate %u pages (%lu total), err=%d\n",
 321                       nreal, obj->base.size / PAGE_SIZE, err);
 322                goto out;
 323        }
 324
 325        mutex_lock(&i915->drm.struct_mutex);
 326        intel_runtime_pm_get(i915);
 327
 328        if (1) {
 329                IGT_TIMEOUT(end);
 330                struct tile tile;
 331
 332                tile.height = 1;
 333                tile.width = 1;
 334                tile.size = 0;
 335                tile.stride = 0;
 336                tile.swizzle = I915_BIT_6_SWIZZLE_NONE;
 337                tile.tiling = I915_TILING_NONE;
 338
 339                err = check_partial_mapping(obj, &tile, end);
 340                if (err && err != -EINTR)
 341                        goto out_unlock;
 342        }
 343
 344        for (tiling = I915_TILING_X; tiling <= I915_TILING_Y; tiling++) {
 345                IGT_TIMEOUT(end);
 346                unsigned int max_pitch;
 347                unsigned int pitch;
 348                struct tile tile;
 349
 350                tile.tiling = tiling;
 351                switch (tiling) {
 352                case I915_TILING_X:
 353                        tile.swizzle = i915->mm.bit_6_swizzle_x;
 354                        break;
 355                case I915_TILING_Y:
 356                        tile.swizzle = i915->mm.bit_6_swizzle_y;
 357                        break;
 358                }
 359
 360                if (tile.swizzle == I915_BIT_6_SWIZZLE_UNKNOWN ||
 361                    tile.swizzle == I915_BIT_6_SWIZZLE_9_10_17)
 362                        continue;
 363
 364                if (INTEL_GEN(i915) <= 2) {
 365                        tile.height = 16;
 366                        tile.width = 128;
 367                        tile.size = 11;
 368                } else if (tile.tiling == I915_TILING_Y &&
 369                           HAS_128_BYTE_Y_TILING(i915)) {
 370                        tile.height = 32;
 371                        tile.width = 128;
 372                        tile.size = 12;
 373                } else {
 374                        tile.height = 8;
 375                        tile.width = 512;
 376                        tile.size = 12;
 377                }
 378
 379                if (INTEL_GEN(i915) < 4)
 380                        max_pitch = 8192 / tile.width;
 381                else if (INTEL_GEN(i915) < 7)
 382                        max_pitch = 128 * I965_FENCE_MAX_PITCH_VAL / tile.width;
 383                else
 384                        max_pitch = 128 * GEN7_FENCE_MAX_PITCH_VAL / tile.width;
 385
 386                for (pitch = max_pitch; pitch; pitch >>= 1) {
 387                        tile.stride = tile.width * pitch;
 388                        err = check_partial_mapping(obj, &tile, end);
 389                        if (err == -EINTR)
 390                                goto next_tiling;
 391                        if (err)
 392                                goto out_unlock;
 393
 394                        if (pitch > 2 && INTEL_GEN(i915) >= 4) {
 395                                tile.stride = tile.width * (pitch - 1);
 396                                err = check_partial_mapping(obj, &tile, end);
 397                                if (err == -EINTR)
 398                                        goto next_tiling;
 399                                if (err)
 400                                        goto out_unlock;
 401                        }
 402
 403                        if (pitch < max_pitch && INTEL_GEN(i915) >= 4) {
 404                                tile.stride = tile.width * (pitch + 1);
 405                                err = check_partial_mapping(obj, &tile, end);
 406                                if (err == -EINTR)
 407                                        goto next_tiling;
 408                                if (err)
 409                                        goto out_unlock;
 410                        }
 411                }
 412
 413                if (INTEL_GEN(i915) >= 4) {
 414                        for_each_prime_number(pitch, max_pitch) {
 415                                tile.stride = tile.width * pitch;
 416                                err = check_partial_mapping(obj, &tile, end);
 417                                if (err == -EINTR)
 418                                        goto next_tiling;
 419                                if (err)
 420                                        goto out_unlock;
 421                        }
 422                }
 423
 424next_tiling: ;
 425        }
 426
 427out_unlock:
 428        intel_runtime_pm_put(i915);
 429        mutex_unlock(&i915->drm.struct_mutex);
 430        i915_gem_object_unpin_pages(obj);
 431out:
 432        i915_gem_object_put(obj);
 433        return err;
 434}
 435
 436static int make_obj_busy(struct drm_i915_gem_object *obj)
 437{
 438        struct drm_i915_private *i915 = to_i915(obj->base.dev);
 439        struct i915_request *rq;
 440        struct i915_vma *vma;
 441        int err;
 442
 443        vma = i915_vma_instance(obj, &i915->ggtt.base, NULL);
 444        if (IS_ERR(vma))
 445                return PTR_ERR(vma);
 446
 447        err = i915_vma_pin(vma, 0, 0, PIN_USER);
 448        if (err)
 449                return err;
 450
 451        rq = i915_request_alloc(i915->engine[RCS], i915->kernel_context);
 452        if (IS_ERR(rq)) {
 453                i915_vma_unpin(vma);
 454                return PTR_ERR(rq);
 455        }
 456
 457        i915_vma_move_to_active(vma, rq, 0);
 458        i915_request_add(rq);
 459
 460        i915_gem_object_set_active_reference(obj);
 461        i915_vma_unpin(vma);
 462        return 0;
 463}
 464
 465static bool assert_mmap_offset(struct drm_i915_private *i915,
 466                               unsigned long size,
 467                               int expected)
 468{
 469        struct drm_i915_gem_object *obj;
 470        int err;
 471
 472        obj = i915_gem_object_create_internal(i915, size);
 473        if (IS_ERR(obj))
 474                return PTR_ERR(obj);
 475
 476        err = i915_gem_object_create_mmap_offset(obj);
 477        i915_gem_object_put(obj);
 478
 479        return err == expected;
 480}
 481
 482static int igt_mmap_offset_exhaustion(void *arg)
 483{
 484        struct drm_i915_private *i915 = arg;
 485        struct drm_mm *mm = &i915->drm.vma_offset_manager->vm_addr_space_mm;
 486        struct drm_i915_gem_object *obj;
 487        struct drm_mm_node resv, *hole;
 488        u64 hole_start, hole_end;
 489        int loop, err;
 490
 491        /* Trim the device mmap space to only a page */
 492        memset(&resv, 0, sizeof(resv));
 493        drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
 494                resv.start = hole_start;
 495                resv.size = hole_end - hole_start - 1; /* PAGE_SIZE units */
 496                err = drm_mm_reserve_node(mm, &resv);
 497                if (err) {
 498                        pr_err("Failed to trim VMA manager, err=%d\n", err);
 499                        return err;
 500                }
 501                break;
 502        }
 503
 504        /* Just fits! */
 505        if (!assert_mmap_offset(i915, PAGE_SIZE, 0)) {
 506                pr_err("Unable to insert object into single page hole\n");
 507                err = -EINVAL;
 508                goto out;
 509        }
 510
 511        /* Too large */
 512        if (!assert_mmap_offset(i915, 2*PAGE_SIZE, -ENOSPC)) {
 513                pr_err("Unexpectedly succeeded in inserting too large object into single page hole\n");
 514                err = -EINVAL;
 515                goto out;
 516        }
 517
 518        /* Fill the hole, further allocation attempts should then fail */
 519        obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
 520        if (IS_ERR(obj)) {
 521                err = PTR_ERR(obj);
 522                goto out;
 523        }
 524
 525        err = i915_gem_object_create_mmap_offset(obj);
 526        if (err) {
 527                pr_err("Unable to insert object into reclaimed hole\n");
 528                goto err_obj;
 529        }
 530
 531        if (!assert_mmap_offset(i915, PAGE_SIZE, -ENOSPC)) {
 532                pr_err("Unexpectedly succeeded in inserting object into no holes!\n");
 533                err = -EINVAL;
 534                goto err_obj;
 535        }
 536
 537        i915_gem_object_put(obj);
 538
 539        /* Now fill with busy dead objects that we expect to reap */
 540        for (loop = 0; loop < 3; loop++) {
 541                obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
 542                if (IS_ERR(obj)) {
 543                        err = PTR_ERR(obj);
 544                        goto out;
 545                }
 546
 547                mutex_lock(&i915->drm.struct_mutex);
 548                intel_runtime_pm_get(i915);
 549                err = make_obj_busy(obj);
 550                intel_runtime_pm_put(i915);
 551                mutex_unlock(&i915->drm.struct_mutex);
 552                if (err) {
 553                        pr_err("[loop %d] Failed to busy the object\n", loop);
 554                        goto err_obj;
 555                }
 556
 557                GEM_BUG_ON(!i915_gem_object_is_active(obj));
 558                err = i915_gem_object_create_mmap_offset(obj);
 559                if (err) {
 560                        pr_err("[loop %d] i915_gem_object_create_mmap_offset failed with err=%d\n",
 561                               loop, err);
 562                        goto out;
 563                }
 564        }
 565
 566out:
 567        drm_mm_remove_node(&resv);
 568        return err;
 569err_obj:
 570        i915_gem_object_put(obj);
 571        goto out;
 572}
 573
 574int i915_gem_object_mock_selftests(void)
 575{
 576        static const struct i915_subtest tests[] = {
 577                SUBTEST(igt_gem_object),
 578                SUBTEST(igt_phys_object),
 579        };
 580        struct drm_i915_private *i915;
 581        int err;
 582
 583        i915 = mock_gem_device();
 584        if (!i915)
 585                return -ENOMEM;
 586
 587        err = i915_subtests(tests, i915);
 588
 589        drm_dev_unref(&i915->drm);
 590        return err;
 591}
 592
 593int i915_gem_object_live_selftests(struct drm_i915_private *i915)
 594{
 595        static const struct i915_subtest tests[] = {
 596                SUBTEST(igt_gem_huge),
 597                SUBTEST(igt_partial_tiling),
 598                SUBTEST(igt_mmap_offset_exhaustion),
 599        };
 600
 601        return i915_subtests(tests, i915);
 602}
 603