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