linux/drivers/gpu/drm/i915/selftests/intel_guc.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2017 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#include "gem/i915_gem_pm.h"
  27
  28/* max doorbell number + negative test for each client type */
  29#define ATTEMPTS (GUC_NUM_DOORBELLS + GUC_CLIENT_PRIORITY_NUM)
  30
  31static struct intel_guc_client *clients[ATTEMPTS];
  32
  33static bool available_dbs(struct intel_guc *guc, u32 priority)
  34{
  35        unsigned long offset;
  36        unsigned long end;
  37        u16 id;
  38
  39        /* first half is used for normal priority, second half for high */
  40        offset = 0;
  41        end = GUC_NUM_DOORBELLS / 2;
  42        if (priority <= GUC_CLIENT_PRIORITY_HIGH) {
  43                offset = end;
  44                end += offset;
  45        }
  46
  47        id = find_next_zero_bit(guc->doorbell_bitmap, end, offset);
  48        if (id < end)
  49                return true;
  50
  51        return false;
  52}
  53
  54static int check_all_doorbells(struct intel_guc *guc)
  55{
  56        u16 db_id;
  57
  58        pr_info_once("Max number of doorbells: %d", GUC_NUM_DOORBELLS);
  59        for (db_id = 0; db_id < GUC_NUM_DOORBELLS; ++db_id) {
  60                if (!doorbell_ok(guc, db_id)) {
  61                        pr_err("doorbell %d, not ok\n", db_id);
  62                        return -EIO;
  63                }
  64        }
  65
  66        return 0;
  67}
  68
  69static int ring_doorbell_nop(struct intel_guc_client *client)
  70{
  71        struct guc_process_desc *desc = __get_process_desc(client);
  72        int err;
  73
  74        client->use_nop_wqi = true;
  75
  76        spin_lock_irq(&client->wq_lock);
  77
  78        guc_wq_item_append(client, 0, 0, 0, 0);
  79        guc_ring_doorbell(client);
  80
  81        spin_unlock_irq(&client->wq_lock);
  82
  83        client->use_nop_wqi = false;
  84
  85        /* if there are no issues GuC will update the WQ head and keep the
  86         * WQ in active status
  87         */
  88        err = wait_for(READ_ONCE(desc->head) == READ_ONCE(desc->tail), 10);
  89        if (err) {
  90                pr_err("doorbell %u ring failed!\n", client->doorbell_id);
  91                return -EIO;
  92        }
  93
  94        if (desc->wq_status != WQ_STATUS_ACTIVE) {
  95                pr_err("doorbell %u ring put WQ in bad state (%u)!\n",
  96                       client->doorbell_id, desc->wq_status);
  97                return -EIO;
  98        }
  99
 100        return 0;
 101}
 102
 103/*
 104 * Basic client sanity check, handy to validate create_clients.
 105 */
 106static int validate_client(struct intel_guc_client *client,
 107                           int client_priority,
 108                           bool is_preempt_client)
 109{
 110        struct drm_i915_private *dev_priv = guc_to_i915(client->guc);
 111        struct i915_gem_context *ctx_owner = is_preempt_client ?
 112                        dev_priv->preempt_context : dev_priv->kernel_context;
 113
 114        if (client->owner != ctx_owner ||
 115            client->engines != INTEL_INFO(dev_priv)->engine_mask ||
 116            client->priority != client_priority ||
 117            client->doorbell_id == GUC_DOORBELL_INVALID)
 118                return -EINVAL;
 119        else
 120                return 0;
 121}
 122
 123static bool client_doorbell_in_sync(struct intel_guc_client *client)
 124{
 125        return !client || doorbell_ok(client->guc, client->doorbell_id);
 126}
 127
 128/*
 129 * Check that we're able to synchronize guc_clients with their doorbells
 130 *
 131 * We're creating clients and reserving doorbells once, at module load. During
 132 * module lifetime, GuC, doorbell HW, and i915 state may go out of sync due to
 133 * GuC being reset. In other words - GuC clients are still around, but the
 134 * status of their doorbells may be incorrect. This is the reason behind
 135 * validating that the doorbells status expected by the driver matches what the
 136 * GuC/HW have.
 137 */
 138static int igt_guc_clients(void *args)
 139{
 140        struct drm_i915_private *dev_priv = args;
 141        intel_wakeref_t wakeref;
 142        struct intel_guc *guc;
 143        int err = 0;
 144
 145        GEM_BUG_ON(!HAS_GUC(dev_priv));
 146        mutex_lock(&dev_priv->drm.struct_mutex);
 147        wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
 148
 149        guc = &dev_priv->guc;
 150        if (!guc) {
 151                pr_err("No guc object!\n");
 152                err = -EINVAL;
 153                goto unlock;
 154        }
 155
 156        err = check_all_doorbells(guc);
 157        if (err)
 158                goto unlock;
 159
 160        /*
 161         * Get rid of clients created during driver load because the test will
 162         * recreate them.
 163         */
 164        guc_clients_disable(guc);
 165        guc_clients_destroy(guc);
 166        if (guc->execbuf_client || guc->preempt_client) {
 167                pr_err("guc_clients_destroy lied!\n");
 168                err = -EINVAL;
 169                goto unlock;
 170        }
 171
 172        err = guc_clients_create(guc);
 173        if (err) {
 174                pr_err("Failed to create clients\n");
 175                goto unlock;
 176        }
 177        GEM_BUG_ON(!guc->execbuf_client);
 178
 179        err = validate_client(guc->execbuf_client,
 180                              GUC_CLIENT_PRIORITY_KMD_NORMAL, false);
 181        if (err) {
 182                pr_err("execbug client validation failed\n");
 183                goto out;
 184        }
 185
 186        if (guc->preempt_client) {
 187                err = validate_client(guc->preempt_client,
 188                                      GUC_CLIENT_PRIORITY_KMD_HIGH, true);
 189                if (err) {
 190                        pr_err("preempt client validation failed\n");
 191                        goto out;
 192                }
 193        }
 194
 195        /* each client should now have reserved a doorbell */
 196        if (!has_doorbell(guc->execbuf_client) ||
 197            (guc->preempt_client && !has_doorbell(guc->preempt_client))) {
 198                pr_err("guc_clients_create didn't reserve doorbells\n");
 199                err = -EINVAL;
 200                goto out;
 201        }
 202
 203        /* Now enable the clients */
 204        guc_clients_enable(guc);
 205
 206        /* each client should now have received a doorbell */
 207        if (!client_doorbell_in_sync(guc->execbuf_client) ||
 208            !client_doorbell_in_sync(guc->preempt_client)) {
 209                pr_err("failed to initialize the doorbells\n");
 210                err = -EINVAL;
 211                goto out;
 212        }
 213
 214        /*
 215         * Basic test - an attempt to reallocate a valid doorbell to the
 216         * client it is currently assigned should not cause a failure.
 217         */
 218        err = create_doorbell(guc->execbuf_client);
 219
 220out:
 221        /*
 222         * Leave clean state for other test, plus the driver always destroy the
 223         * clients during unload.
 224         */
 225        guc_clients_disable(guc);
 226        guc_clients_destroy(guc);
 227        guc_clients_create(guc);
 228        guc_clients_enable(guc);
 229unlock:
 230        intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
 231        mutex_unlock(&dev_priv->drm.struct_mutex);
 232        return err;
 233}
 234
 235/*
 236 * Create as many clients as number of doorbells. Note that there's already
 237 * client(s)/doorbell(s) created during driver load, but this test creates
 238 * its own and do not interact with the existing ones.
 239 */
 240static int igt_guc_doorbells(void *arg)
 241{
 242        struct drm_i915_private *dev_priv = arg;
 243        intel_wakeref_t wakeref;
 244        struct intel_guc *guc;
 245        int i, err = 0;
 246        u16 db_id;
 247
 248        GEM_BUG_ON(!HAS_GUC(dev_priv));
 249        mutex_lock(&dev_priv->drm.struct_mutex);
 250        wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
 251
 252        guc = &dev_priv->guc;
 253        if (!guc) {
 254                pr_err("No guc object!\n");
 255                err = -EINVAL;
 256                goto unlock;
 257        }
 258
 259        err = check_all_doorbells(guc);
 260        if (err)
 261                goto unlock;
 262
 263        for (i = 0; i < ATTEMPTS; i++) {
 264                clients[i] = guc_client_alloc(dev_priv,
 265                                              INTEL_INFO(dev_priv)->engine_mask,
 266                                              i % GUC_CLIENT_PRIORITY_NUM,
 267                                              dev_priv->kernel_context);
 268
 269                if (!clients[i]) {
 270                        pr_err("[%d] No guc client\n", i);
 271                        err = -EINVAL;
 272                        goto out;
 273                }
 274
 275                if (IS_ERR(clients[i])) {
 276                        if (PTR_ERR(clients[i]) != -ENOSPC) {
 277                                pr_err("[%d] unexpected error\n", i);
 278                                err = PTR_ERR(clients[i]);
 279                                goto out;
 280                        }
 281
 282                        if (available_dbs(guc, i % GUC_CLIENT_PRIORITY_NUM)) {
 283                                pr_err("[%d] non-db related alloc fail\n", i);
 284                                err = -EINVAL;
 285                                goto out;
 286                        }
 287
 288                        /* expected, ran out of dbs for this client type */
 289                        continue;
 290                }
 291
 292                /*
 293                 * The check below is only valid because we keep a doorbell
 294                 * assigned during the whole life of the client.
 295                 */
 296                if (clients[i]->stage_id >= GUC_NUM_DOORBELLS) {
 297                        pr_err("[%d] more clients than doorbells (%d >= %d)\n",
 298                               i, clients[i]->stage_id, GUC_NUM_DOORBELLS);
 299                        err = -EINVAL;
 300                        goto out;
 301                }
 302
 303                err = validate_client(clients[i],
 304                                      i % GUC_CLIENT_PRIORITY_NUM, false);
 305                if (err) {
 306                        pr_err("[%d] client_alloc sanity check failed!\n", i);
 307                        err = -EINVAL;
 308                        goto out;
 309                }
 310
 311                db_id = clients[i]->doorbell_id;
 312
 313                err = __guc_client_enable(clients[i]);
 314                if (err) {
 315                        pr_err("[%d] Failed to create a doorbell\n", i);
 316                        goto out;
 317                }
 318
 319                /* doorbell id shouldn't change, we are holding the mutex */
 320                if (db_id != clients[i]->doorbell_id) {
 321                        pr_err("[%d] doorbell id changed (%d != %d)\n",
 322                               i, db_id, clients[i]->doorbell_id);
 323                        err = -EINVAL;
 324                        goto out;
 325                }
 326
 327                err = check_all_doorbells(guc);
 328                if (err)
 329                        goto out;
 330
 331                err = ring_doorbell_nop(clients[i]);
 332                if (err)
 333                        goto out;
 334        }
 335
 336out:
 337        for (i = 0; i < ATTEMPTS; i++)
 338                if (!IS_ERR_OR_NULL(clients[i])) {
 339                        __guc_client_disable(clients[i]);
 340                        guc_client_free(clients[i]);
 341                }
 342unlock:
 343        intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
 344        mutex_unlock(&dev_priv->drm.struct_mutex);
 345        return err;
 346}
 347
 348int intel_guc_live_selftest(struct drm_i915_private *dev_priv)
 349{
 350        static const struct i915_subtest tests[] = {
 351                SUBTEST(igt_guc_clients),
 352                SUBTEST(igt_guc_doorbells),
 353        };
 354
 355        if (!USES_GUC_SUBMISSION(dev_priv))
 356                return 0;
 357
 358        return i915_subtests(tests, dev_priv);
 359}
 360