root/drivers/gpu/drm/i915/gt/uc/selftest_guc.c

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

DEFINITIONS

This source file includes following definitions.
  1. available_dbs
  2. check_all_doorbells
  3. ring_doorbell_nop
  4. validate_client
  5. client_doorbell_in_sync
  6. igt_guc_clients
  7. igt_guc_doorbells
  8. intel_guc_live_selftest

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

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