1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2023-2024 Intel Corporation 4 */ 5 6 #include <linux/debugfs.h> 7 8 #include <drm/drm_print.h> 9 #include <drm/drm_debugfs.h> 10 11 #include "xe_bo.h" 12 #include "xe_debugfs.h" 13 #include "xe_device.h" 14 #include "xe_gt.h" 15 #include "xe_gt_debugfs.h" 16 #include "xe_gt_sriov_pf_config.h" 17 #include "xe_gt_sriov_pf_control.h" 18 #include "xe_gt_sriov_pf_debugfs.h" 19 #include "xe_gt_sriov_pf_helpers.h" 20 #include "xe_gt_sriov_pf_migration.h" 21 #include "xe_gt_sriov_pf_monitor.h" 22 #include "xe_gt_sriov_pf_policy.h" 23 #include "xe_gt_sriov_pf_service.h" 24 #include "xe_pm.h" 25 26 /* 27 * /sys/kernel/debug/dri/0/ 28 * ├── gt0 # d_inode->i_private = gt 29 * │ ├── pf # d_inode->i_private = gt 30 * │ ├── vf1 # d_inode->i_private = VFID(1) 31 * : : 32 * │ ├── vfN # d_inode->i_private = VFID(N) 33 */ 34 35 static void *extract_priv(struct dentry *d) 36 { 37 return d->d_inode->i_private; 38 } 39 40 static struct xe_gt *extract_gt(struct dentry *d) 41 { 42 return extract_priv(d->d_parent); 43 } 44 45 static unsigned int extract_vfid(struct dentry *d) 46 { 47 return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d); 48 } 49 50 /* 51 * /sys/kernel/debug/dri/0/ 52 * ├── gt0 53 * │ ├── pf 54 * │ │ ├── ggtt_available 55 * │ │ ├── ggtt_provisioned 56 * │ │ ├── contexts_provisioned 57 * │ │ ├── doorbells_provisioned 58 * │ │ ├── runtime_registers 59 * │ │ ├── negotiated_versions 60 * │ │ ├── adverse_events 61 */ 62 63 static const struct drm_info_list pf_info[] = { 64 { 65 "ggtt_available", 66 .show = xe_gt_debugfs_simple_show, 67 .data = xe_gt_sriov_pf_config_print_available_ggtt, 68 }, 69 { 70 "ggtt_provisioned", 71 .show = xe_gt_debugfs_simple_show, 72 .data = xe_gt_sriov_pf_config_print_ggtt, 73 }, 74 { 75 "contexts_provisioned", 76 .show = xe_gt_debugfs_simple_show, 77 .data = xe_gt_sriov_pf_config_print_ctxs, 78 }, 79 { 80 "doorbells_provisioned", 81 .show = xe_gt_debugfs_simple_show, 82 .data = xe_gt_sriov_pf_config_print_dbs, 83 }, 84 { 85 "lmem_provisioned", 86 .show = xe_gt_debugfs_simple_show, 87 .data = xe_gt_sriov_pf_config_print_lmem, 88 }, 89 { 90 "runtime_registers", 91 .show = xe_gt_debugfs_simple_show, 92 .data = xe_gt_sriov_pf_service_print_runtime, 93 }, 94 { 95 "negotiated_versions", 96 .show = xe_gt_debugfs_simple_show, 97 .data = xe_gt_sriov_pf_service_print_version, 98 }, 99 { 100 "adverse_events", 101 .show = xe_gt_debugfs_simple_show, 102 .data = xe_gt_sriov_pf_monitor_print_events, 103 }, 104 }; 105 106 /* 107 * /sys/kernel/debug/dri/0/ 108 * ├── gt0 109 * │ ├── pf 110 * │ │ ├── reset_engine 111 * │ │ ├── sample_period 112 * │ │ ├── sched_if_idle 113 */ 114 115 #define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT) \ 116 \ 117 static int POLICY##_set(void *data, u64 val) \ 118 { \ 119 struct xe_gt *gt = extract_gt(data); \ 120 struct xe_device *xe = gt_to_xe(gt); \ 121 int err; \ 122 \ 123 if (val > (TYPE)~0ull) \ 124 return -EOVERFLOW; \ 125 \ 126 xe_pm_runtime_get(xe); \ 127 err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \ 128 xe_pm_runtime_put(xe); \ 129 \ 130 return err; \ 131 } \ 132 \ 133 static int POLICY##_get(void *data, u64 *val) \ 134 { \ 135 struct xe_gt *gt = extract_gt(data); \ 136 \ 137 *val = xe_gt_sriov_pf_policy_get_##POLICY(gt); \ 138 return 0; \ 139 } \ 140 \ 141 DEFINE_DEBUGFS_ATTRIBUTE(POLICY##_fops, POLICY##_get, POLICY##_set, FORMAT) 142 143 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(reset_engine, bool, "%llu\n"); 144 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sched_if_idle, bool, "%llu\n"); 145 DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(sample_period, u32, "%llu\n"); 146 147 static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent) 148 { 149 xe_gt_assert(gt, gt == extract_gt(parent)); 150 xe_gt_assert(gt, PFID == extract_vfid(parent)); 151 152 debugfs_create_file_unsafe("reset_engine", 0644, parent, parent, &reset_engine_fops); 153 debugfs_create_file_unsafe("sched_if_idle", 0644, parent, parent, &sched_if_idle_fops); 154 debugfs_create_file_unsafe("sample_period_ms", 0644, parent, parent, &sample_period_fops); 155 } 156 157 /* 158 * /sys/kernel/debug/dri/0/ 159 * ├── gt0 160 * │ ├── pf 161 * │ │ ├── ggtt_spare 162 * │ │ ├── lmem_spare 163 * │ │ ├── doorbells_spare 164 * │ │ ├── contexts_spare 165 * │ │ ├── exec_quantum_ms 166 * │ │ ├── preempt_timeout_us 167 * │ │ ├── sched_priority 168 * │ ├── vf1 169 * │ │ ├── ggtt_quota 170 * │ │ ├── lmem_quota 171 * │ │ ├── doorbells_quota 172 * │ │ ├── contexts_quota 173 * │ │ ├── exec_quantum_ms 174 * │ │ ├── preempt_timeout_us 175 * │ │ ├── sched_priority 176 */ 177 178 #define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \ 179 \ 180 static int CONFIG##_set(void *data, u64 val) \ 181 { \ 182 struct xe_gt *gt = extract_gt(data); \ 183 unsigned int vfid = extract_vfid(data); \ 184 struct xe_device *xe = gt_to_xe(gt); \ 185 int err; \ 186 \ 187 if (val > (TYPE)~0ull) \ 188 return -EOVERFLOW; \ 189 \ 190 xe_pm_runtime_get(xe); \ 191 err = xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \ 192 xe_pm_runtime_put(xe); \ 193 \ 194 return err; \ 195 } \ 196 \ 197 static int CONFIG##_get(void *data, u64 *val) \ 198 { \ 199 struct xe_gt *gt = extract_gt(data); \ 200 unsigned int vfid = extract_vfid(data); \ 201 \ 202 *val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid); \ 203 return 0; \ 204 } \ 205 \ 206 DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT) 207 208 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n"); 209 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n"); 210 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n"); 211 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n"); 212 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n"); 213 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n"); 214 DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(sched_priority, u32, "%llu\n"); 215 216 /* 217 * /sys/kernel/debug/dri/0/ 218 * ├── gt0 219 * │ ├── pf 220 * │ │ ├── threshold_cat_error_count 221 * │ │ ├── threshold_doorbell_time_us 222 * │ │ ├── threshold_engine_reset_count 223 * │ │ ├── threshold_guc_time_us 224 * │ │ ├── threshold_irq_time_us 225 * │ │ ├── threshold_page_fault_count 226 * │ ├── vf1 227 * │ │ ├── threshold_cat_error_count 228 * │ │ ├── threshold_doorbell_time_us 229 * │ │ ├── threshold_engine_reset_count 230 * │ │ ├── threshold_guc_time_us 231 * │ │ ├── threshold_irq_time_us 232 * │ │ ├── threshold_page_fault_count 233 */ 234 235 static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index) 236 { 237 struct xe_gt *gt = extract_gt(data); 238 unsigned int vfid = extract_vfid(data); 239 struct xe_device *xe = gt_to_xe(gt); 240 int err; 241 242 if (val > (u32)~0ull) 243 return -EOVERFLOW; 244 245 xe_pm_runtime_get(xe); 246 err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val); 247 xe_pm_runtime_put(xe); 248 249 return err; 250 } 251 252 static int get_threshold(void *data, u64 *val, enum xe_guc_klv_threshold_index index) 253 { 254 struct xe_gt *gt = extract_gt(data); 255 unsigned int vfid = extract_vfid(data); 256 257 *val = xe_gt_sriov_pf_config_get_threshold(gt, vfid, index); 258 return 0; 259 } 260 261 #define DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(THRESHOLD, INDEX) \ 262 \ 263 static int THRESHOLD##_set(void *data, u64 val) \ 264 { \ 265 return set_threshold(data, val, INDEX); \ 266 } \ 267 \ 268 static int THRESHOLD##_get(void *data, u64 *val) \ 269 { \ 270 return get_threshold(data, val, INDEX); \ 271 } \ 272 \ 273 DEFINE_DEBUGFS_ATTRIBUTE(THRESHOLD##_fops, THRESHOLD##_get, THRESHOLD##_set, "%llu\n") 274 275 /* generate all threshold attributes */ 276 #define define_threshold_attribute(TAG, NAME, ...) \ 277 DEFINE_SRIOV_GT_THRESHOLD_DEBUGFS_ATTRIBUTE(NAME, MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)); 278 MAKE_XE_GUC_KLV_THRESHOLDS_SET(define_threshold_attribute) 279 #undef define_threshold_attribute 280 281 static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigned int vfid) 282 { 283 xe_gt_assert(gt, gt == extract_gt(parent)); 284 xe_gt_assert(gt, vfid == extract_vfid(parent)); 285 286 if (!xe_gt_is_media_type(gt)) { 287 debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare", 288 0644, parent, parent, &ggtt_fops); 289 if (IS_DGFX(gt_to_xe(gt))) 290 debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare", 291 0644, parent, parent, &lmem_fops); 292 } 293 debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare", 294 0644, parent, parent, &dbs_fops); 295 debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare", 296 0644, parent, parent, &ctxs_fops); 297 debugfs_create_file_unsafe("exec_quantum_ms", 0644, parent, parent, 298 &exec_quantum_fops); 299 debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent, 300 &preempt_timeout_fops); 301 debugfs_create_file_unsafe("sched_priority", 0644, parent, parent, 302 &sched_priority_fops); 303 304 /* register all threshold attributes */ 305 #define register_threshold_attribute(TAG, NAME, ...) \ 306 debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \ 307 &NAME##_fops); 308 MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute) 309 #undef register_threshold_attribute 310 } 311 312 /* 313 * /sys/kernel/debug/dri/0/ 314 * ├── gt0 315 * │ ├── vf1 316 * │ │ ├── control { stop, pause, resume } 317 */ 318 319 static const struct { 320 const char *cmd; 321 int (*fn)(struct xe_gt *gt, unsigned int vfid); 322 } control_cmds[] = { 323 { "stop", xe_gt_sriov_pf_control_stop_vf }, 324 { "pause", xe_gt_sriov_pf_control_pause_vf }, 325 { "resume", xe_gt_sriov_pf_control_resume_vf }, 326 #ifdef CONFIG_DRM_XE_DEBUG_SRIOV 327 { "restore!", xe_gt_sriov_pf_migration_restore_guc_state }, 328 #endif 329 }; 330 331 static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) 332 { 333 struct dentry *dent = file_dentry(file); 334 struct dentry *parent = dent->d_parent; 335 struct xe_gt *gt = extract_gt(parent); 336 struct xe_device *xe = gt_to_xe(gt); 337 unsigned int vfid = extract_vfid(parent); 338 int ret = -EINVAL; 339 char cmd[32]; 340 size_t n; 341 342 xe_gt_assert(gt, vfid); 343 xe_gt_sriov_pf_assert_vfid(gt, vfid); 344 345 if (*pos) 346 return -ESPIPE; 347 348 if (count > sizeof(cmd) - 1) 349 return -EINVAL; 350 351 ret = simple_write_to_buffer(cmd, sizeof(cmd) - 1, pos, buf, count); 352 if (ret < 0) 353 return ret; 354 cmd[ret] = '\0'; 355 356 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) { 357 xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd)); 358 359 if (sysfs_streq(cmd, control_cmds[n].cmd)) { 360 xe_pm_runtime_get(xe); 361 ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0; 362 xe_pm_runtime_put(xe); 363 break; 364 } 365 } 366 367 return (ret < 0) ? ret : count; 368 } 369 370 static ssize_t control_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 371 { 372 char help[128]; 373 size_t n; 374 375 help[0] = '\0'; 376 for (n = 0; n < ARRAY_SIZE(control_cmds); n++) { 377 strlcat(help, control_cmds[n].cmd, sizeof(help)); 378 strlcat(help, "\n", sizeof(help)); 379 } 380 381 return simple_read_from_buffer(buf, count, ppos, help, strlen(help)); 382 } 383 384 static const struct file_operations control_ops = { 385 .owner = THIS_MODULE, 386 .open = simple_open, 387 .write = control_write, 388 .read = control_read, 389 .llseek = default_llseek, 390 }; 391 392 /* 393 * /sys/kernel/debug/dri/0/ 394 * ├── gt0 395 * │ ├── vf1 396 * │ │ ├── guc_state 397 */ 398 static ssize_t guc_state_read(struct file *file, char __user *buf, 399 size_t count, loff_t *pos) 400 { 401 struct dentry *dent = file_dentry(file); 402 struct dentry *parent = dent->d_parent; 403 struct xe_gt *gt = extract_gt(parent); 404 unsigned int vfid = extract_vfid(parent); 405 406 return xe_gt_sriov_pf_migration_read_guc_state(gt, vfid, buf, count, pos); 407 } 408 409 static ssize_t guc_state_write(struct file *file, const char __user *buf, 410 size_t count, loff_t *pos) 411 { 412 struct dentry *dent = file_dentry(file); 413 struct dentry *parent = dent->d_parent; 414 struct xe_gt *gt = extract_gt(parent); 415 unsigned int vfid = extract_vfid(parent); 416 417 if (*pos) 418 return -EINVAL; 419 420 return xe_gt_sriov_pf_migration_write_guc_state(gt, vfid, buf, count); 421 } 422 423 static const struct file_operations guc_state_ops = { 424 .owner = THIS_MODULE, 425 .read = guc_state_read, 426 .write = guc_state_write, 427 .llseek = default_llseek, 428 }; 429 430 /* 431 * /sys/kernel/debug/dri/0/ 432 * ├── gt0 433 * │ ├── vf1 434 * │ │ ├── config_blob 435 */ 436 static ssize_t config_blob_read(struct file *file, char __user *buf, 437 size_t count, loff_t *pos) 438 { 439 struct dentry *dent = file_dentry(file); 440 struct dentry *parent = dent->d_parent; 441 struct xe_gt *gt = extract_gt(parent); 442 unsigned int vfid = extract_vfid(parent); 443 ssize_t ret; 444 void *tmp; 445 446 ret = xe_gt_sriov_pf_config_save(gt, vfid, NULL, 0); 447 if (!ret) 448 return -ENODATA; 449 if (ret < 0) 450 return ret; 451 452 tmp = kzalloc(ret, GFP_KERNEL); 453 if (!tmp) 454 return -ENOMEM; 455 456 ret = xe_gt_sriov_pf_config_save(gt, vfid, tmp, ret); 457 if (ret > 0) 458 ret = simple_read_from_buffer(buf, count, pos, tmp, ret); 459 460 kfree(tmp); 461 return ret; 462 } 463 464 static ssize_t config_blob_write(struct file *file, const char __user *buf, 465 size_t count, loff_t *pos) 466 { 467 struct dentry *dent = file_dentry(file); 468 struct dentry *parent = dent->d_parent; 469 struct xe_gt *gt = extract_gt(parent); 470 unsigned int vfid = extract_vfid(parent); 471 ssize_t ret; 472 void *tmp; 473 474 if (*pos) 475 return -EINVAL; 476 477 if (!count) 478 return -ENODATA; 479 480 if (count > SZ_4K) 481 return -EINVAL; 482 483 tmp = kzalloc(count, GFP_KERNEL); 484 if (!tmp) 485 return -ENOMEM; 486 487 if (copy_from_user(tmp, buf, count)) { 488 ret = -EFAULT; 489 } else { 490 ret = xe_gt_sriov_pf_config_restore(gt, vfid, tmp, count); 491 if (!ret) 492 ret = count; 493 } 494 kfree(tmp); 495 return ret; 496 } 497 498 static const struct file_operations config_blob_ops = { 499 .owner = THIS_MODULE, 500 .read = config_blob_read, 501 .write = config_blob_write, 502 .llseek = default_llseek, 503 }; 504 505 /** 506 * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs. 507 * @gt: the &xe_gt to register 508 * @root: the &dentry that represents the GT directory 509 * 510 * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs. 511 */ 512 void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root) 513 { 514 struct xe_device *xe = gt_to_xe(gt); 515 struct drm_minor *minor = xe->drm.primary; 516 int n, totalvfs = xe_sriov_pf_get_totalvfs(xe); 517 struct dentry *pfdentry; 518 struct dentry *vfdentry; 519 char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */ 520 521 xe_gt_assert(gt, IS_SRIOV_PF(xe)); 522 xe_gt_assert(gt, root->d_inode->i_private == gt); 523 524 /* 525 * /sys/kernel/debug/dri/0/ 526 * ├── gt0 527 * │ ├── pf 528 */ 529 pfdentry = debugfs_create_dir("pf", root); 530 if (IS_ERR(pfdentry)) 531 return; 532 pfdentry->d_inode->i_private = gt; 533 534 drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor); 535 pf_add_policy_attrs(gt, pfdentry); 536 pf_add_config_attrs(gt, pfdentry, PFID); 537 538 for (n = 1; n <= totalvfs; n++) { 539 /* 540 * /sys/kernel/debug/dri/0/ 541 * ├── gt0 542 * │ ├── vf1 543 * │ ├── vf2 544 */ 545 snprintf(buf, sizeof(buf), "vf%u", n); 546 vfdentry = debugfs_create_dir(buf, root); 547 if (IS_ERR(vfdentry)) 548 break; 549 vfdentry->d_inode->i_private = (void *)(uintptr_t)n; 550 551 pf_add_config_attrs(gt, vfdentry, VFID(n)); 552 debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops); 553 554 /* for testing/debugging purposes only! */ 555 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) { 556 debugfs_create_file("guc_state", 557 IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400, 558 vfdentry, NULL, &guc_state_ops); 559 debugfs_create_file("config_blob", 560 IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400, 561 vfdentry, NULL, &config_blob_ops); 562 } 563 } 564 } 565